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 //==============================================================================
36 // Declare just the minimum number of interfaces for the DSound objects that we need..
37 typedef struct typeDSBUFFERDESC
43 LPWAVEFORMATEX lpwfxFormat
;
47 struct IDirectSoundBuffer
;
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
;
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
100 LPWAVEFORMATEX lpwfxFormat
;
103 struct IDirectSoundCaptureBuffer
;
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
;
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 //==============================================================================
142 String
getDSErrorMessage (HRESULT hr
)
144 const char* result
= nullptr;
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
);
171 //==============================================================================
172 #define DS_DEBUGGING 1
175 #define CATCH JUCE_CATCH_EXCEPTION
177 #define log(a) Logger::writeToLog(a);
179 #define logError(a) logDSError(a, __LINE__);
181 static void logDSError (HRESULT hr
, int lineNum
)
185 String
error ("DS error at line ");
186 error
<< lineNum
<< " - " << getDSErrorMessage (hr
);
191 #define CATCH JUCE_CATCH_ALL
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
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()
248 if (pOutputBuffer
!= nullptr)
250 log ("closing dsound out: " + name
);
251 hr
= pOutputBuffer
->Stop();
254 hr
= pOutputBuffer
->Release();
255 pOutputBuffer
= nullptr;
259 if (pDirectSound
!= nullptr)
261 hr
= pDirectSound
->Release();
262 pDirectSound
= nullptr;
269 log ("opening dsound out device: " + name
+ " rate=" + String (sampleRate
)
270 + " bits=" + String (bitDepth
) + " buf=" + String (bufferSizeSamples
));
272 pDirectSound
= nullptr;
273 pOutputBuffer
= nullptr;
277 HRESULT hr
= E_NOINTERFACE
;
279 if (dsDirectSoundCreate
!= 0)
280 hr
= dsDirectSoundCreate (&guid
, &pDirectSound
, 0);
284 bytesPerBuffer
= (bufferSizeSamples
* (bitDepth
>> 2)) & ~15;
285 totalBytesPerBuffer
= (3 * bytesPerBuffer
) & ~15;
286 const int numChannels
= 2;
288 hr
= pDirectSound
->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
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);
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
;
316 hr
= pPrimaryBuffer
->SetFormat (&wfFormat
);
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);
333 log ("opening dsound out step 3");
336 unsigned char* pDSBuffData
;
338 hr
= pOutputBuffer
->Lock (0, totalBytesPerBuffer
,
339 (LPVOID
*) &pDSBuffData
, &dwDataLen
, 0, 0, 0);
344 zeromem (pDSBuffData
, dwDataLen
);
346 hr
= pOutputBuffer
->Unlock (pDSBuffData
, dwDataLen
, 0, 0);
350 hr
= pOutputBuffer
->SetCurrentPosition (0);
354 hr
= pOutputBuffer
->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
357 return String::empty
;
367 error
= getDSErrorMessage (hr
);
372 void synchronisePosition()
374 if (pOutputBuffer
!= nullptr)
377 pOutputBuffer
->GetCurrentPosition (&playCursor
, &writeOffset
);
383 if (pOutputBuffer
== 0)
386 DWORD playCursor
, writeCursor
;
390 HRESULT hr
= pOutputBuffer
->GetCurrentPosition (&playCursor
, &writeCursor
);
392 if (hr
== MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
394 pOutputBuffer
->Restore();
406 int playWriteGap
= writeCursor
- playCursor
;
407 if (playWriteGap
< 0)
408 playWriteGap
+= totalBytesPerBuffer
;
410 int bytesEmpty
= playCursor
- writeOffset
;
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;
428 HRESULT hr
= pOutputBuffer
->Lock (writeOffset
, bytesPerBuffer
,
430 &lpbuf2
, &dwSize2
, 0);
432 if (hr
== MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
434 pOutputBuffer
->Restore();
436 hr
= pOutputBuffer
->Lock (writeOffset
, bytesPerBuffer
,
438 &lpbuf2
, &dwSize2
, 0);
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;
453 while (--samples1
>= 0)
454 *dest
++ = (convertInputValue (*right
++) << 16);
456 dest
= static_cast<int*> (lpbuf2
);
458 while (--samples2
>= 0)
459 *dest
++ = (convertInputValue (*right
++) << 16);
463 while (--samples1
>= 0)
464 *dest
++ = (0xffff & convertInputValue (*left
++));
466 dest
= static_cast<int*> (lpbuf2
);
468 while (--samples2
>= 0)
469 *dest
++ = (0xffff & convertInputValue (*left
++));
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
);
495 writeOffset
= (writeOffset
+ dwSize1
+ dwSize2
) % totalBytesPerBuffer
;
497 pOutputBuffer
->Unlock (lpbuf1
, dwSize1
, lpbuf2
, dwSize2
);
505 bytesEmpty
-= bytesPerBuffer
;
520 int sampleRate
, bufferSizeSamples
;
524 IDirectSound
* pDirectSound
;
525 IDirectSoundBuffer
* pOutputBuffer
;
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
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()
559 if (pInputBuffer
!= nullptr)
561 log ("closing dsound in: " + name
);
562 hr
= pInputBuffer
->Stop();
565 hr
= pInputBuffer
->Release();
566 pInputBuffer
= nullptr;
570 if (pDirectSoundCapture
!= nullptr)
572 hr
= pDirectSoundCapture
->Release();
573 pDirectSoundCapture
= nullptr;
577 if (pDirectSound
!= nullptr)
579 hr
= pDirectSound
->Release();
580 pDirectSound
= nullptr;
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;
594 totalBytesPerBuffer
= 0;
597 HRESULT hr
= E_NOINTERFACE
;
599 if (dsDirectSoundCaptureCreate
!= 0)
600 hr
= dsDirectSoundCaptureCreate (&guid
, &pDirectSoundCapture
, 0);
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
;
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);
632 hr
= pInputBuffer
->Start (1 /* DSCBSTART_LOOPING */);
636 return String::empty
;
640 error
= getDSErrorMessage (hr
);
646 void synchronisePosition()
648 if (pInputBuffer
!= nullptr)
651 pInputBuffer
->GetCurrentPosition (&capturePos
, (DWORD
*)&readOffset
);
657 if (pInputBuffer
== 0)
660 DWORD capturePos
, readPos
;
661 HRESULT hr
= pInputBuffer
->GetCurrentPosition (&capturePos
, &readPos
);
667 int bytesFilled
= readPos
- readOffset
;
669 bytesFilled
+= totalBytesPerBuffer
;
671 if (bytesFilled
>= bytesPerBuffer
)
673 LPBYTE lpbuf1
= nullptr;
674 LPBYTE lpbuf2
= nullptr;
678 HRESULT hr
= pInputBuffer
->Lock (readOffset
, bytesPerBuffer
,
679 (void**) &lpbuf1
, &dwsize1
,
680 (void**) &lpbuf2
, &dwsize2
, 0);
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
;
697 while (--samples1
>= 0)
700 *destR
++ = *src
++ * g
;
703 src
= (const short*)lpbuf2
;
705 while (--samples2
>= 0)
708 *destR
++ = *src
++ * g
;
713 while (--samples1
>= 0)
715 *destL
++ = *src
++ * g
;
719 src
= (const short*)lpbuf2
;
721 while (--samples2
>= 0)
723 *destL
++ = *src
++ * g
;
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
;
749 readOffset
= (readOffset
+ dwsize1
+ dwsize2
) % totalBytesPerBuffer
;
751 pInputBuffer
->Unlock (lpbuf1
, dwsize1
, lpbuf2
, dwsize2
);
759 bytesFilled
-= bytesPerBuffer
;
769 unsigned int readOffset
;
770 int bytesPerBuffer
, totalBytesPerBuffer
;
777 int sampleRate
, bufferSizeSamples
;
781 IDirectSound
* pDirectSound
;
782 IDirectSoundCapture
* pDirectSoundCapture
;
783 IDirectSoundCaptureBuffer
* pInputBuffer
;
785 JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel
);
788 //==============================================================================
789 class DSoundAudioIODevice
: public AudioIODevice
,
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_
),
802 bufferSizeSamples (0),
806 outputBuffers (1, 1),
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()
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();
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
)
871 for (int i
= 0; i
< index
; ++i
)
874 : ((n
< 2048) ? 128 : 256));
879 int getCurrentBitDepth()
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
);
895 void start (AudioIODeviceCallback
* call
)
897 if (isOpen_
&& call
!= nullptr && ! isStarted
)
899 if (! isThreadRunning())
901 // something gone wrong and the thread's stopped..
906 call
->audioDeviceAboutToStart (this);
908 const ScopedLock
sl (startStopLock
);
918 AudioIODeviceCallback
* const callbackLocal
= callback
;
921 const ScopedLock
sl (startStopLock
);
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
;
942 OwnedArray
<DSoundInternalInChannel
> inChans
;
943 OwnedArray
<DSoundInternalOutChannel
> outChans
;
944 WaitableEvent startEvent
;
946 int bufferSizeSamples
;
947 int volatile totalSamplesOut
;
948 int64
volatile lastBlockTime
;
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_
);
967 inputBuffers
.setSize (1, 1);
968 outputBuffers
.setSize (1, 1);
973 if (! threadShouldExit())
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();
989 while (! threadShouldExit())
995 const int latencyMs
= (int) (bufferSizeSamples
* 1000.0 / sampleRate
);
996 const int maxTimeMS
= jmax (5, 3 * latencyMs
);
998 while (! threadShouldExit())
1001 uint32 startTime
= Time::getMillisecondCounter();
1004 for (i
= inChans
.size(); --i
>= 0;)
1006 inChans
.getUnchecked(i
)->doneFlag
= false;
1010 for (i
= outChans
.size(); --i
>= 0;)
1012 outChans
.getUnchecked(i
)->doneFlag
= false;
1018 const int maxCount
= 3;
1019 int count
= maxCount
;
1023 for (i
= inChans
.size(); --i
>= 0;)
1025 DSoundInternalInChannel
* const in
= inChans
.getUnchecked(i
);
1027 if ((! in
->doneFlag
) && in
->service())
1029 in
->doneFlag
= true;
1034 for (i
= outChans
.size(); --i
>= 0;)
1036 DSoundInternalOutChannel
* const out
= outChans
.getUnchecked(i
);
1038 if ((! out
->doneFlag
) && out
->service())
1040 out
->doneFlag
= true;
1048 if (Time::getMillisecondCounter() > startTime
+ maxTimeMS
)
1060 if (threadShouldExit())
1069 const ScopedLock
sl (startStopLock
);
1075 callback
->audioDeviceIOCallback (const_cast <const float**> (inputBuffers
.getArrayOfChannels()),
1076 inputBuffers
.getNumChannels(),
1077 outputBuffers
.getArrayOfChannels(),
1078 outputBuffers
.getNumChannels(),
1081 JUCE_CATCH_EXCEPTION
1083 totalSamplesOut
+= bufferSizeSamples
;
1087 outputBuffers
.clear();
1088 totalSamplesOut
= 0;
1094 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice
);
1097 //==============================================================================
1098 struct DSoundDeviceList
1100 StringArray outputDeviceNames
, inputDeviceNames
;
1101 Array
<GUID
> outputGuids
, inputGuids
;
1105 outputDeviceNames
.clear();
1106 inputDeviceNames
.clear();
1107 outputGuids
.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
;
1126 static BOOL
enumProc (LPGUID lpGUID
, String desc
, StringArray
& names
, Array
<GUID
>& guids
)
1130 if (desc
.isNotEmpty())
1132 const String
origDesc (desc
);
1135 while (names
.contains (desc
))
1136 desc
= origDesc
+ " (" + String (n
++) + ")";
1139 guids
.add (lpGUID
!= nullptr ? *lpGUID
: GUID());
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_
)
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
;
1177 enabledInputs
= inputChannels
;
1178 enabledInputs
.setRange (inChannels
.size(),
1179 enabledInputs
.getHighestBit() + 1 - inChannels
.size(),
1182 inputBuffers
.setSize (jmax (1, enabledInputs
.countNumberOfSetBits()), bufferSizeSamples
);
1183 inputBuffers
.clear();
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
,
1203 enabledOutputs
= outputChannels
;
1204 enabledOutputs
.setRange (outChannels
.size(),
1205 enabledOutputs
.getHighestBit() + 1 - outChannels
.size(),
1208 outputBuffers
.setSize (jmax (1, enabledOutputs
.countNumberOfSetBits()), bufferSizeSamples
);
1209 outputBuffers
.clear();
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
,
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
+ "\"";
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
+ "\"";
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();
1282 SetThreadPriority (GetCurrentThread(), oldThreadPri
);
1283 SetPriorityClass (GetCurrentProcess(), oldProcPri
);
1288 //==============================================================================
1289 class DSoundAudioIODeviceType
: public AudioIODeviceType
,
1290 private DeviceChangeDetector
1293 DSoundAudioIODeviceType()
1294 : AudioIODeviceType ("DirectSound"),
1295 DeviceChangeDetector (L
"DirectSound"),
1298 initialiseDSoundFunctions();
1301 //==============================================================================
1302 void scanForDevices()
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
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
);
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
1347 outputIndex
, inputIndex
);
1353 //==============================================================================
1354 DSoundDeviceList deviceList
;
1357 void systemDeviceChanged()
1359 DSoundDeviceList newList
;
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();