1 #ifdef _WIN32
2 /*
3  * $Id$
4  * Portable Audio I/O Library DirectSound implementation
5  *
6  * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
7  * Based on the Open Source API proposed by Ross Bencina
8  * Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining
11  * a copy of this software and associated documentation files
12  * (the "Software"), to deal in the Software without restriction,
13  * including without limitation the rights to use, copy, modify, merge,
14  * publish, distribute, sublicense, and/or sell copies of the Software,
15  * and to permit persons to whom the Software is furnished to do so,
16  * subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be
19  * included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
25  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
26  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28  */
29 
30 /*
31  * The text above constitutes the entire PortAudio license; however,
32  * the PortAudio community also makes the following non-binding requests:
33  *
34  * Any person wishing to distribute modifications to the Software is
35  * requested to send the modifications to the original developer so that
36  * they can be incorporated into the canonical version. It is also
37  * requested that these non-binding requests be included along with the
38  * license above.
39  */
40 
41 /** @file
42  @ingroup hostapi_src
43 */
44 
45 /* Until May 2011 PA/DS has used a multimedia timer to perform the callback.
46    We're replacing this with a new implementation using a thread and a different timer mechanim.
47    Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
48 */
49 //#define PA_WIN_DS_USE_WMME_TIMER
50 
51 #include <assert.h>
52 #include <stdio.h>
53 #include <string.h> /* strlen() */
54 
55 #define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
56 #include <initguid.h> /* make sure ds guids get defined */
57 #include <windows.h>
58 #include <objbase.h>
59 
60 
61 /*
62   Use the earliest version of DX required, no need to polute the namespace
63 */
64 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
65 #define DIRECTSOUND_VERSION 0x0800
66 #else
67 #define DIRECTSOUND_VERSION 0x0300
68 #endif
69 #include <dsound.h>
70 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
71 #include <dsconf.h>
72 #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
73 #ifndef PA_WIN_DS_USE_WMME_TIMER
74 #ifndef UNDER_CE
75 #include <process.h>
76 #endif
77 #endif
78 
79 #include "pa_util.h"
80 #include "pa_allocation.h"
81 #include "pa_hostapi.h"
82 #include "pa_stream.h"
83 #include "pa_cpuload.h"
84 #include "pa_process.h"
85 #include "pa_debugprint.h"
86 
87 #include "pa_win_ds.h"
88 #include "pa_win_ds_dynlink.h"
89 #include "pa_win_waveformat.h"
90 #include "pa_win_wdmks_utils.h"
91 #include "pa_win_coinitialize.h"
92 
93 #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
94 #pragma comment( lib, "dsound.lib" )
95 #pragma comment( lib, "winmm.lib" )
96 #pragma comment( lib, "kernel32.lib" )
97 #endif
98 
99 /* use CreateThread for CYGWIN, _beginthreadex for all others */
100 #ifndef PA_WIN_DS_USE_WMME_TIMER
101 
102 #if !defined(__CYGWIN__) && !defined(UNDER_CE)
103 #define CREATE_THREAD (HANDLE)_beginthreadex
104 #undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */
105 #define PA_THREAD_FUNC static unsigned WINAPI
106 #define PA_THREAD_ID unsigned
107 #else
108 #define CREATE_THREAD CreateThread
109 #define CLOSE_THREAD_HANDLE CloseHandle
110 #define PA_THREAD_FUNC static DWORD WINAPI
111 #define PA_THREAD_ID DWORD
112 #endif
113 
114 #if (defined(UNDER_CE))
115 #pragma comment(lib, "Coredll.lib")
116 #elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
117 #pragma comment(lib, "winmm.lib")
118 #endif
119 
120 PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
121 
122 #if !defined(UNDER_CE)
123 #define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */
124 #endif
125 
126 #endif /* !PA_WIN_DS_USE_WMME_TIMER */
127 
128 
129 /*
130  provided in newer platform sdks and x64
131  */
132 #ifndef DWORD_PTR
133     #if defined(_WIN64)
134         #define DWORD_PTR unsigned __int64
135     #else
136         #define DWORD_PTR unsigned long
137     #endif
138 #endif
139 
140 #define PRINT(x) PA_DEBUG(x);
141 #define ERR_RPT(x) PRINT(x)
142 #define DBUG(x)   PRINT(x)
143 #define DBUGX(x)  PRINT(x)
144 
145 #define PA_USE_HIGH_LATENCY   (0)
146 #if PA_USE_HIGH_LATENCY
147 #define PA_DS_WIN_9X_DEFAULT_LATENCY_     (.500)
148 #define PA_DS_WIN_NT_DEFAULT_LATENCY_     (.600)
149 #else
150 #define PA_DS_WIN_9X_DEFAULT_LATENCY_     (.140)
151 #define PA_DS_WIN_NT_DEFAULT_LATENCY_     (.280)
152 #endif
153 
154 #define PA_DS_WIN_WDM_DEFAULT_LATENCY_    (.120)
155 
156 /* we allow the polling period to range between 1 and 100ms.
157    prior to August 2011 we limited the minimum polling period to 10ms.
158 */
159 #define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS    (0.001) /* 1ms */
160 #define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS    (0.100) /* 100ms */
161 #define PA_DS_POLLING_JITTER_SECONDS            (0.001) /* 1ms */
162 
163 #define SECONDS_PER_MSEC      (0.001)
164 #define MSECS_PER_SECOND       (1000)
165 
166 /* prototypes for functions declared in this file */
167 
168 #ifdef __cplusplus
169 extern "C"
170 {
171 #endif /* __cplusplus */
172 
173 PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
174 
175 #ifdef __cplusplus
176 }
177 #endif /* __cplusplus */
178 
179 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
180 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
181                            PaStream** s,
182                            const PaStreamParameters *inputParameters,
183                            const PaStreamParameters *outputParameters,
184                            double sampleRate,
185                            unsigned long framesPerBuffer,
186                            PaStreamFlags streamFlags,
187                            PaStreamCallback *streamCallback,
188                            void *userData );
189 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
190                                   const PaStreamParameters *inputParameters,
191                                   const PaStreamParameters *outputParameters,
192                                   double sampleRate );
193 static PaError CloseStream( PaStream* stream );
194 static PaError StartStream( PaStream *stream );
195 static PaError StopStream( PaStream *stream );
196 static PaError AbortStream( PaStream *stream );
197 static PaError IsStreamStopped( PaStream *s );
198 static PaError IsStreamActive( PaStream *stream );
199 static PaTime GetStreamTime( PaStream *stream );
200 static double GetStreamCpuLoad( PaStream* stream );
201 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
202 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
203 static signed long GetStreamReadAvailable( PaStream* stream );
204 static signed long GetStreamWriteAvailable( PaStream* stream );
205 
206 
207 /* FIXME: should convert hr to a string */
208 #define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
209     PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
210 
211 /************************************************* DX Prototypes **********/
212 static BOOL CALLBACK CollectGUIDsProcW(LPGUID lpGUID,
213                                      LPCWSTR lpszDesc,
214                                      LPCWSTR lpszDrvName,
215                                      LPVOID lpContext );
216 
217 /************************************************************************************/
218 /********************** Structures **************************************************/
219 /************************************************************************************/
220 /* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
221 
222 typedef struct PaWinDsDeviceInfo
223 {
224     PaDeviceInfo        inheritedDeviceInfo;
225     GUID                guid;
226     GUID                *lpGUID;
227     double              sampleRates[3];
228     char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
229     char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
230 } PaWinDsDeviceInfo;
231 
232 typedef struct
233 {
234     PaUtilHostApiRepresentation inheritedHostApiRep;
235     PaUtilStreamInterface    callbackStreamInterface;
236     PaUtilStreamInterface    blockingStreamInterface;
237 
238     PaUtilAllocationGroup   *allocations;
239 
240     /* implementation specific data goes here */
241 
242     PaWinUtilComInitializationResult comInitializationResult;
243 
244 } PaWinDsHostApiRepresentation;
245 
246 
247 /* PaWinDsStream - a stream data structure specifically for this implementation */
248 
249 typedef struct PaWinDsStream
250 {
251     PaUtilStreamRepresentation streamRepresentation;
252     PaUtilCpuLoadMeasurer cpuLoadMeasurer;
253     PaUtilBufferProcessor bufferProcessor;
254 
255 /* DirectSound specific data. */
256 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
257     LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
258 #endif
259 
260 /* Output */
261     LPDIRECTSOUND        pDirectSound;
262     LPDIRECTSOUNDBUFFER  pDirectSoundPrimaryBuffer;
263     LPDIRECTSOUNDBUFFER  pDirectSoundOutputBuffer;
264     DWORD                outputBufferWriteOffsetBytes;     /* last write position */
265     INT                  outputBufferSizeBytes;
266     INT                  outputFrameSizeBytes;
267     /* Try to detect play buffer underflows. */
268     LARGE_INTEGER        perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
269     LARGE_INTEGER        previousPlayTime;
270     DWORD                previousPlayCursor;
271     UINT                 outputUnderflowCount;
272     BOOL                 outputIsRunning;
273     INT                  finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
274 
275 /* Input */
276     LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
277     LPDIRECTSOUNDCAPTUREBUFFER   pDirectSoundInputBuffer;
278     INT                  inputFrameSizeBytes;
279     UINT                 readOffset;      /* last read position */
280     UINT                 inputBufferSizeBytes;
281 
282 
283     int              hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
284     double           framesWritten;
285     double           secondsPerHostByte; /* Used to optimize latency calculation for outTime */
286     double           pollingPeriodSeconds;
287 
288     PaStreamCallbackFlags callbackFlags;
289 
290     PaStreamFlags    streamFlags;
291     int              callbackResult;
292     HANDLE           processingCompleted;
293 
294 /* FIXME - move all below to PaUtilStreamRepresentation */
295     volatile int     isStarted;
296     volatile int     isActive;
297     volatile int     stopProcessing; /* stop thread once existing buffers have been returned */
298     volatile int     abortProcessing; /* stop thread immediately */
299 
300     UINT             systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
301 
302 #ifdef PA_WIN_DS_USE_WMME_TIMER
303     MMRESULT         timerID;
304 #else
305 
306 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
307     HANDLE           waitableTimer;
308 #endif
309     HANDLE           processingThread;
310     PA_THREAD_ID     processingThreadId;
311     HANDLE           processingThreadCompleted;
312 #endif
313 
314 } PaWinDsStream;
315 
316 
317 /* Set minimal latency based on the current OS version.
318  * NT has higher latency.
319  */
PaWinDS_GetMinSystemLatencySeconds(void)320 static double PaWinDS_GetMinSystemLatencySeconds( void )
321 {
322 /*
323 NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
324 versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
325 Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
326 is is faster, for now we just disable the deprecation warning.
327 See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
328 See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
329 */
330 #pragma warning (disable : 4996) /* use of GetVersionEx */
331 
332     double minLatencySeconds;
333     /* Set minimal latency based on whether NT or other OS.
334      * NT has higher latency.
335      */
336 
337     OSVERSIONINFO osvi;
338     osvi.dwOSVersionInfoSize = sizeof( osvi );
339     GetVersionEx( &osvi );
340     DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
341     DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
342     DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
343     /* Check for NT */
344     if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
345     {
346         minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
347     }
348     else if(osvi.dwMajorVersion >= 5)
349     {
350         minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
351     }
352     else
353     {
354         minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
355     }
356     return minLatencySeconds;
357 
358 #pragma warning (default : 4996)
359 }
360 
361 
362 /*************************************************************************
363 ** Return minimum workable latency required for this host. This is returned
364 ** As the default stream latency in PaDeviceInfo.
365 ** Latency can be optionally set by user by setting an environment variable.
366 ** For example, to set latency to 200 msec, put:
367 **
368 **    set PA_MIN_LATENCY_MSEC=200
369 **
370 ** in the AUTOEXEC.BAT file and reboot.
371 ** If the environment variable is not set, then the latency will be determined
372 ** based on the OS. Windows NT has higher latency than Win95.
373 */
374 #define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
375 #define PA_ENV_BUF_SIZE  (32)
376 
PaWinDs_GetMinLatencySeconds(double sampleRate)377 static double PaWinDs_GetMinLatencySeconds( double sampleRate )
378 {
379     char      envbuf[PA_ENV_BUF_SIZE];
380     DWORD     hresult;
381     double    minLatencySeconds = 0;
382 
383     /* Let user determine minimal latency by setting environment variable. */
384     hresult = GetEnvironmentVariableA( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
385     if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
386     {
387         minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
388     }
389     else
390     {
391         minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
392 #if PA_USE_HIGH_LATENCY
393         PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
394 #endif
395     }
396 
397     return minLatencySeconds;
398 }
399 
400 
401 /************************************************************************************
402 ** Duplicate and convert the input string using the group allocations allocator.
403 ** A NULL string is converted to a zero length string.
404 ** If memory cannot be allocated, NULL is returned.
405 **/
DuplicateDeviceNameString(PaUtilAllocationGroup * allocations,const wchar_t * src)406 static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const wchar_t* src )
407 {
408     char *result = 0;
409 
410     if( src != NULL )
411     {
412 #if !defined(_UNICODE) && !defined(UNICODE)
413         size_t len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
414 
415         result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
416         if( result ) {
417             if (WideCharToMultiByte(CP_ACP, 0, src, -1, result, (int)len, NULL, NULL) == 0) {
418                 result = 0;
419             }
420         }
421 #else
422         size_t len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
423 
424         result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
425         if( result ) {
426             if (WideCharToMultiByte(CP_UTF8, 0, src, -1, result, (int)len, NULL, NULL) == 0) {
427                 result = 0;
428             }
429         }
430 #endif
431     }
432     else
433     {
434         result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
435         if( result )
436             result[0] = '\0';
437     }
438 
439     return result;
440 }
441 
442 /************************************************************************************
443 ** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
444 ** information during device enumeration.
445 */
446 typedef struct DSDeviceNameAndGUID{
447     char *name; // allocated from parent's allocations, never deleted by this structure
448     GUID guid;
449     LPGUID lpGUID;
450     void *pnpInterface;  // wchar_t* interface path, allocated using the DS host api's allocation group
451 } DSDeviceNameAndGUID;
452 
453 typedef struct DSDeviceNameAndGUIDVector{
454     PaUtilAllocationGroup *allocations;
455     PaError enumerationError;
456 
457     int count;
458     int free;
459     DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
460 } DSDeviceNameAndGUIDVector;
461 
462 typedef struct DSDeviceNamesAndGUIDs{
463     PaWinDsHostApiRepresentation *winDsHostApi;
464     DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
465     DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
466 } DSDeviceNamesAndGUIDs;
467 
InitializeDSDeviceNameAndGUIDVector(DSDeviceNameAndGUIDVector * guidVector,PaUtilAllocationGroup * allocations)468 static PaError InitializeDSDeviceNameAndGUIDVector(
469         DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
470 {
471     PaError result = paNoError;
472 
473     guidVector->allocations = allocations;
474     guidVector->enumerationError = paNoError;
475 
476     guidVector->count = 0;
477     guidVector->free = 8;
478     guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
479     if( guidVector->items == NULL )
480         result = paInsufficientMemory;
481 
482     return result;
483 }
484 
ExpandDSDeviceNameAndGUIDVector(DSDeviceNameAndGUIDVector * guidVector)485 static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
486 {
487     PaError result = paNoError;
488     DSDeviceNameAndGUID *newItems;
489     int i;
490 
491     /* double size of vector */
492     int size = guidVector->count + guidVector->free;
493     guidVector->free += size;
494 
495     newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
496     if( newItems == NULL )
497     {
498         result = paInsufficientMemory;
499     }
500     else
501     {
502         for( i=0; i < guidVector->count; ++i )
503         {
504             newItems[i].name = guidVector->items[i].name;
505             if( guidVector->items[i].lpGUID == NULL )
506             {
507                 newItems[i].lpGUID = NULL;
508             }
509             else
510             {
511                 newItems[i].lpGUID = &newItems[i].guid;
512                 memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );
513             }
514             newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
515         }
516 
517         LocalFree( guidVector->items );
518         guidVector->items = newItems;
519     }
520 
521     return result;
522 }
523 
524 /*
525     it's safe to call DSDeviceNameAndGUIDVector multiple times
526 */
TerminateDSDeviceNameAndGUIDVector(DSDeviceNameAndGUIDVector * guidVector)527 static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
528 {
529     PaError result = paNoError;
530 
531     if( guidVector->items != NULL )
532     {
533         if( LocalFree( guidVector->items ) != NULL )
534             result = paInsufficientMemory;              /** @todo this isn't the correct error to return from a deallocation failure */
535 
536         guidVector->items = NULL;
537     }
538 
539     return result;
540 }
541 
542 /************************************************************************************
543 ** Collect preliminary device information during DirectSound enumeration
544 */
CollectGUIDsProcW(LPGUID lpGUID,LPCWSTR lpszDesc,LPCWSTR lpszDrvName,LPVOID lpContext)545 static BOOL CALLBACK CollectGUIDsProcW(LPGUID lpGUID,
546                                      LPCWSTR lpszDesc,
547                                      LPCWSTR lpszDrvName,
548                                      LPVOID lpContext )
549 {
550     DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
551     PaError error;
552 
553     (void) lpszDrvName; /* unused variable */
554 
555     if( namesAndGUIDs->free == 0 )
556     {
557         error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
558         if( error != paNoError )
559         {
560             namesAndGUIDs->enumerationError = error;
561             return FALSE;
562         }
563     }
564 
565     /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
566     if( lpGUID == NULL )
567     {
568         namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
569     }
570     else
571     {
572         namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
573                 &namesAndGUIDs->items[namesAndGUIDs->count].guid;
574 
575         memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
576     }
577 
578     namesAndGUIDs->items[namesAndGUIDs->count].name =
579             DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
580     if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
581     {
582         namesAndGUIDs->enumerationError = paInsufficientMemory;
583         return FALSE;
584     }
585 
586     namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
587 
588     ++namesAndGUIDs->count;
589     --namesAndGUIDs->free;
590 
591     return TRUE;
592 }
593 
594 
595 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
596 
DuplicateWCharString(PaUtilAllocationGroup * allocations,wchar_t * source)597 static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
598 {
599     size_t len;
600     wchar_t *result;
601 
602     len = wcslen( source );
603     result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
604     wcscpy( result, source );
605     return result;
606 }
607 
KsPropertySetEnumerateCallback(PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data,LPVOID context)608 static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
609 {
610     int i;
611     DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
612 
613     /*
614         Apparently data->Interface can be NULL in some cases.
615         Possibly virtual devices without hardware.
616         So we check for NULLs now. See mailing list message November 10, 2012:
617         "[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
618     */
619     if( data->Interface )
620     {
621         if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
622         {
623             for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
624             {
625                 if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
626                     && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
627                 {
628                     deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
629                         (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
630                     break;
631                 }
632             }
633         }
634         else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
635         {
636             for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
637             {
638                 if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
639                     && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
640                 {
641                     deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
642                         (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
643                     break;
644                 }
645             }
646         }
647     }
648 
649     return TRUE;
650 }
651 
652 
653 static GUID pawin_CLSID_DirectSoundPrivate =
654 { 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
655 
656 static GUID pawin_DSPROPSETID_DirectSoundDevice =
657 { 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
658 
659 static GUID pawin_IID_IKsPropertySet =
660 { 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
661 
662 
663 /*
664     FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
665     with UNICODE file paths to the devices. The DS documentation mentions
666     at least two techniques by which these Interface paths can be found using IKsPropertySet on
667     the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
668     property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
669     I tried both methods and only the second worked. I found two postings on the
670     net from people who had the same problem with the first method, so I think the method used here is
671     more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
672     but the fields of the device description are not filled in.
673 
674     The mechanism we use works by registering an enumeration callback which is called for
675     every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
676     with the matching GUID and copies the pointer to the Interface path.
677     Note that we could have used this enumeration callback to perform the original
678     device enumeration, however we choose not to so we can disable this step easily.
679 
680     Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
681     http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
682 
683         -- rossb
684 */
FindDevicePnpInterfaces(DSDeviceNamesAndGUIDs * deviceNamesAndGUIDs)685 static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
686 {
687     IClassFactory *pClassFactory;
688 
689     if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
690         IKsPropertySet *pPropertySet;
691         if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
692 
693             DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
694             ULONG bytesReturned;
695 
696             data.Callback = KsPropertySetEnumerateCallback;
697             data.Context = deviceNamesAndGUIDs;
698 
699             IKsPropertySet_Get( pPropertySet,
700                 &pawin_DSPROPSETID_DirectSoundDevice,
701                 DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
702                 NULL,
703                 0,
704                 &data,
705                 sizeof(data),
706                 &bytesReturned
707             );
708 
709             IKsPropertySet_Release( pPropertySet );
710         }
711         pClassFactory->lpVtbl->Release( pClassFactory );
712     }
713 
714     /*
715         The following code fragment, which I chose not to use, queries for the
716         device interface for a device with a specific GUID:
717 
718         ULONG BytesReturned;
719         DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
720 
721         memset (&Property, 0, sizeof(Property));
722         Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
723         Property.DeviceId = *lpGUID;
724 
725         hr = IKsPropertySet_Get( pPropertySet,
726             &pawin_DSPROPSETID_DirectSoundDevice,
727             DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
728             NULL,
729             0,
730             &Property,
731             sizeof(Property),
732             &BytesReturned
733         );
734 
735         if( hr == S_OK )
736         {
737             //pnpInterface = Property.Interface;
738         }
739     */
740 }
741 #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
742 
743 
744 /*
745     GUIDs for emulated devices which we blacklist below.
746     are there more than two of them??
747 */
748 
749 GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
750 GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
751 
752 
753 #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
754 static double defaultSampleRateSearchOrder_[] =
755     { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
756         16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
757 
758 /************************************************************************************
759 ** Extract capabilities from an output device, and add it to the device info list
760 ** if successful. This function assumes that there is enough room in the
761 ** device info list to accomodate all entries.
762 **
763 ** The device will not be added to the device list if any errors are encountered.
764 */
AddOutputDeviceInfoFromDirectSound(PaWinDsHostApiRepresentation * winDsHostApi,char * name,LPGUID lpGUID,char * pnpInterface)765 static PaError AddOutputDeviceInfoFromDirectSound(
766         PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
767 {
768     PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
769     PaWinDsDeviceInfo            *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
770     PaDeviceInfo                 *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
771     HRESULT                       hr;
772     LPDIRECTSOUND                 lpDirectSound;
773     DSCAPS                        caps;
774     int                           deviceOK = TRUE;
775     PaError                       result = paNoError;
776     int                           i;
777 
778     /* Copy GUID to the device info structure. Set pointer. */
779     if( lpGUID == NULL )
780     {
781         winDsDeviceInfo->lpGUID = NULL;
782     }
783     else
784     {
785         memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
786         winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
787     }
788 
789     if( lpGUID )
790     {
791         if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
792             IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
793         {
794             PA_DEBUG(("BLACKLISTED: %s \n",name));
795             return paNoError;
796         }
797     }
798 
799     /* Create a DirectSound object for the specified GUID
800         Note that using CoCreateInstance doesn't work on windows CE.
801     */
802     hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
803 
804     /** try using CoCreateInstance because DirectSoundCreate was hanging under
805         some circumstances - note this was probably related to the
806         #define BOOL short bug which has now been fixed
807         @todo delete this comment and the following code once we've ensured
808         there is no bug.
809     */
810     /*
811     hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
812             &IID_IDirectSound, (void**)&lpDirectSound );
813 
814     if( hr == S_OK )
815     {
816         hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
817     }
818     */
819 
820     if( hr != DS_OK )
821     {
822         if (hr == DSERR_ALLOCATED)
823             PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
824         DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
825         if (lpGUID)
826             DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n",
827                  name,
828                  lpGUID->Data1,
829                  lpGUID->Data2,
830                  lpGUID->Data3,
831                  lpGUID->Data4[0],
832                  lpGUID->Data4[1],
833                  lpGUID->Data4[2],
834                  lpGUID->Data4[3],
835                  lpGUID->Data4[4],
836                  lpGUID->Data4[5],
837                  lpGUID->Data4[6],
838                  lpGUID->Data4[7]));
839 
840         deviceOK = FALSE;
841     }
842     else
843     {
844         /* Query device characteristics. */
845         memset( &caps, 0, sizeof(caps) );
846         caps.dwSize = sizeof(caps);
847         hr = IDirectSound_GetCaps( lpDirectSound, &caps );
848         if( hr != DS_OK )
849         {
850             DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
851             deviceOK = FALSE;
852         }
853         else
854         {
855 
856 #if PA_USE_WMME
857             if( caps.dwFlags & DSCAPS_EMULDRIVER )
858             {
859                 /* If WMME supported, then reject Emulated drivers because they are lousy. */
860                 deviceOK = FALSE;
861             }
862 #endif
863 
864             if( deviceOK )
865             {
866                 deviceInfo->maxInputChannels = 0;
867                 winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
868 
869                 /* DS output capabilities only indicate supported number of channels
870                    using two flags which indicate mono and/or stereo.
871                    We assume that stereo devices may support more than 2 channels
872                    (as is the case with 5.1 devices for example) and so
873                    set deviceOutputChannelCountIsKnown to 0 (unknown).
874                    In this case OpenStream will try to open the device
875                    when the user requests more than 2 channels, rather than
876                    returning an error.
877                 */
878                 if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
879                 {
880                     deviceInfo->maxOutputChannels = 2;
881                     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
882                 }
883                 else
884                 {
885                     deviceInfo->maxOutputChannels = 1;
886                     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
887                 }
888 
889                 /* Guess channels count from speaker configuration. We do it only when
890                    pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
891                 */
892 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
893                 if( !pnpInterface )
894 #endif
895                 {
896                     DWORD spkrcfg;
897                     if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
898                     {
899                         int count = 0;
900                         switch (DSSPEAKER_CONFIG(spkrcfg))
901                         {
902                             case DSSPEAKER_HEADPHONE:        count = 2; break;
903                             case DSSPEAKER_MONO:             count = 1; break;
904                             case DSSPEAKER_QUAD:             count = 4; break;
905                             case DSSPEAKER_STEREO:           count = 2; break;
906                             case DSSPEAKER_SURROUND:         count = 4; break;
907                             case DSSPEAKER_5POINT1:          count = 6; break;
908 #ifndef DSSPEAKER_7POINT1
909 #define DSSPEAKER_7POINT1 0x00000007
910 #endif
911                             case DSSPEAKER_7POINT1:          count = 8; break;
912 #ifndef DSSPEAKER_7POINT1_SURROUND
913 #define DSSPEAKER_7POINT1_SURROUND 0x00000008
914 #endif
915                             case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
916 #ifndef DSSPEAKER_5POINT1_SURROUND
917 #define DSSPEAKER_5POINT1_SURROUND 0x00000009
918 #endif
919                             case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
920                         }
921                         if( count )
922                         {
923                             deviceInfo->maxOutputChannels = count;
924                             winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
925                         }
926                     }
927                 }
928 
929 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
930                 if( pnpInterface )
931                 {
932                     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0  );
933                     if( count > 0 )
934                     {
935                         deviceInfo->maxOutputChannels = count;
936                         winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
937                     }
938                 }
939 #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
940 
941                 /* initialize defaultSampleRate */
942 
943                 if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
944                 {
945                     /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
946                     deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
947 
948                     for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
949                     {
950                         if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
951                                 && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
952                         {
953                             deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
954                             break;
955                         }
956                     }
957                 }
958                 else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
959                 {
960                     if( caps.dwMinSecondarySampleRate == 0 )
961                     {
962                         /*
963                         ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
964                         ** But it supports continuous sampling.
965                         ** So fake range of rates, and hope it really supports it.
966                         */
967                         deviceInfo->defaultSampleRate = 48000.0f;  /* assume 48000 as the default */
968 
969                         DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
970                     }
971                     else
972                     {
973                         deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
974                     }
975                 }
976                 else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
977                 {
978                     /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
979                     ** But we know that they really support a range of rates!
980                     ** So when we see a ridiculous set of rates, assume it is a range.
981                     */
982                   deviceInfo->defaultSampleRate = 48000.0f;  /* assume 48000 as the default */
983                   DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
984                 }
985                 else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
986 
987                 //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
988                 // dwFlags | DSCAPS_CONTINUOUSRATE
989 
990                 deviceInfo->defaultLowInputLatency = 0.;
991                 deviceInfo->defaultHighInputLatency = 0.;
992 
993                 deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
994                 deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
995             }
996         }
997 
998         IDirectSound_Release( lpDirectSound );
999     }
1000 
1001     if( deviceOK )
1002     {
1003         deviceInfo->name = name;
1004 
1005         if( lpGUID == NULL )
1006             hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
1007 
1008         hostApi->info.deviceCount++;
1009     }
1010 
1011     return result;
1012 }
1013 
1014 
1015 /************************************************************************************
1016 ** Extract capabilities from an input device, and add it to the device info list
1017 ** if successful. This function assumes that there is enough room in the
1018 ** device info list to accomodate all entries.
1019 **
1020 ** The device will not be added to the device list if any errors are encountered.
1021 */
AddInputDeviceInfoFromDirectSoundCapture(PaWinDsHostApiRepresentation * winDsHostApi,char * name,LPGUID lpGUID,char * pnpInterface)1022 static PaError AddInputDeviceInfoFromDirectSoundCapture(
1023         PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
1024 {
1025     PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
1026     PaWinDsDeviceInfo            *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
1027     PaDeviceInfo                 *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
1028     HRESULT                       hr;
1029     LPDIRECTSOUNDCAPTURE          lpDirectSoundCapture;
1030     DSCCAPS                       caps;
1031     int                           deviceOK = TRUE;
1032     PaError                       result = paNoError;
1033 
1034     /* Copy GUID to the device info structure. Set pointer. */
1035     if( lpGUID == NULL )
1036     {
1037         winDsDeviceInfo->lpGUID = NULL;
1038     }
1039     else
1040     {
1041         winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
1042         memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
1043     }
1044 
1045     hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
1046 
1047     /** try using CoCreateInstance because DirectSoundCreate was hanging under
1048         some circumstances - note this was probably related to the
1049         #define BOOL short bug which has now been fixed
1050         @todo delete this comment and the following code once we've ensured
1051         there is no bug.
1052     */
1053     /*
1054     hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
1055             &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
1056     */
1057     if( hr != DS_OK )
1058     {
1059         DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
1060         deviceOK = FALSE;
1061     }
1062     else
1063     {
1064         /* Query device characteristics. */
1065         memset( &caps, 0, sizeof(caps) );
1066         caps.dwSize = sizeof(caps);
1067         hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
1068         if( hr != DS_OK )
1069         {
1070             DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
1071             deviceOK = FALSE;
1072         }
1073         else
1074         {
1075 #if PA_USE_WMME
1076             if( caps.dwFlags & DSCAPS_EMULDRIVER )
1077             {
1078                 /* If WMME supported, then reject Emulated drivers because they are lousy. */
1079                 deviceOK = FALSE;
1080             }
1081 #endif
1082 
1083             if( deviceOK )
1084             {
1085                 deviceInfo->maxInputChannels = caps.dwChannels;
1086                 winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
1087 
1088                 deviceInfo->maxOutputChannels = 0;
1089                 winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1090 
1091 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1092                 if( pnpInterface )
1093                 {
1094                     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1  );
1095                     if( count > 0 )
1096                     {
1097                         deviceInfo->maxInputChannels = count;
1098                         winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
1099                     }
1100                 }
1101 #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1102 
1103 /*  constants from a WINE patch by Francois Gouget, see:
1104     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
1105 
1106     ---
1107     Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
1108     From: Francois Gouget <fgouget@ ... .fr>
1109     To: Ross Bencina <rbencina@ ... .au>
1110     Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
1111 
1112     [snip]
1113 
1114     I give you permission to use the patch below under the BSD license.
1115     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
1116 
1117     [snip]
1118 */
1119 #ifndef WAVE_FORMAT_48M08
1120 #define WAVE_FORMAT_48M08      0x00001000    /* 48     kHz, Mono,   8-bit  */
1121 #define WAVE_FORMAT_48S08      0x00002000    /* 48     kHz, Stereo, 8-bit  */
1122 #define WAVE_FORMAT_48M16      0x00004000    /* 48     kHz, Mono,   16-bit */
1123 #define WAVE_FORMAT_48S16      0x00008000    /* 48     kHz, Stereo, 16-bit */
1124 #define WAVE_FORMAT_96M08      0x00010000    /* 96     kHz, Mono,   8-bit  */
1125 #define WAVE_FORMAT_96S08      0x00020000    /* 96     kHz, Stereo, 8-bit  */
1126 #define WAVE_FORMAT_96M16      0x00040000    /* 96     kHz, Mono,   16-bit */
1127 #define WAVE_FORMAT_96S16      0x00080000    /* 96     kHz, Stereo, 16-bit */
1128 #endif
1129 
1130                 /* defaultSampleRate */
1131                 if( caps.dwChannels == 2 )
1132                 {
1133                     if( caps.dwFormats & WAVE_FORMAT_4S16 )
1134                         deviceInfo->defaultSampleRate = 44100.0;
1135                     else if( caps.dwFormats & WAVE_FORMAT_48S16 )
1136                         deviceInfo->defaultSampleRate = 48000.0;
1137                     else if( caps.dwFormats & WAVE_FORMAT_2S16 )
1138                         deviceInfo->defaultSampleRate = 22050.0;
1139                     else if( caps.dwFormats & WAVE_FORMAT_1S16 )
1140                         deviceInfo->defaultSampleRate = 11025.0;
1141                     else if( caps.dwFormats & WAVE_FORMAT_96S16 )
1142                         deviceInfo->defaultSampleRate = 96000.0;
1143                     else
1144                         deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1145                 }
1146                 else if( caps.dwChannels == 1 )
1147                 {
1148                     if( caps.dwFormats & WAVE_FORMAT_4M16 )
1149                         deviceInfo->defaultSampleRate = 44100.0;
1150                     else if( caps.dwFormats & WAVE_FORMAT_48M16 )
1151                         deviceInfo->defaultSampleRate = 48000.0;
1152                     else if( caps.dwFormats & WAVE_FORMAT_2M16 )
1153                         deviceInfo->defaultSampleRate = 22050.0;
1154                     else if( caps.dwFormats & WAVE_FORMAT_1M16 )
1155                         deviceInfo->defaultSampleRate = 11025.0;
1156                     else if( caps.dwFormats & WAVE_FORMAT_96M16 )
1157                         deviceInfo->defaultSampleRate = 96000.0;
1158                     else
1159                         deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1160                 }
1161                 else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1162 
1163                 deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
1164                 deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
1165 
1166                 deviceInfo->defaultLowOutputLatency = 0.;
1167                 deviceInfo->defaultHighOutputLatency = 0.;
1168             }
1169         }
1170 
1171         IDirectSoundCapture_Release( lpDirectSoundCapture );
1172     }
1173 
1174     if( deviceOK )
1175     {
1176         deviceInfo->name = name;
1177 
1178         if( lpGUID == NULL )
1179             hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
1180 
1181         hostApi->info.deviceCount++;
1182     }
1183 
1184     return result;
1185 }
1186 
1187 
1188 /***********************************************************************************/
PaWinDs_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)1189 PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1190 {
1191     PaError result = paNoError;
1192     int i, deviceCount;
1193     PaWinDsHostApiRepresentation *winDsHostApi;
1194     DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
1195     PaWinDsDeviceInfo *deviceInfoArray;
1196 
1197     PaWinDs_InitializeDSoundEntryPoints();
1198 
1199     /* initialise guid vectors so they can be safely deleted on error */
1200     deviceNamesAndGUIDs.winDsHostApi = NULL;
1201     deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
1202     deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
1203 
1204     winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
1205     if( !winDsHostApi )
1206     {
1207         result = paInsufficientMemory;
1208         goto error;
1209     }
1210 
1211     memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */
1212 
1213     result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
1214     if( result != paNoError )
1215     {
1216         goto error;
1217     }
1218 
1219     winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
1220     if( !winDsHostApi->allocations )
1221     {
1222         result = paInsufficientMemory;
1223         goto error;
1224     }
1225 
1226     *hostApi = &winDsHostApi->inheritedHostApiRep;
1227     (*hostApi)->info.structVersion = 1;
1228     (*hostApi)->info.type = paDirectSound;
1229     (*hostApi)->info.name = "Windows DirectSound";
1230 
1231     (*hostApi)->info.deviceCount = 0;
1232     (*hostApi)->info.defaultInputDevice = paNoDevice;
1233     (*hostApi)->info.defaultOutputDevice = paNoDevice;
1234 
1235 
1236 /* DSound - enumerate devices to count them and to gather their GUIDs */
1237 
1238     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
1239     if( result != paNoError )
1240         goto error;
1241 
1242     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
1243     if( result != paNoError )
1244         goto error;
1245 
1246     paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
1247 
1248     paWinDsDSoundEntryPoints.DirectSoundEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
1249 
1250     if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
1251     {
1252         result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
1253         goto error;
1254     }
1255 
1256     if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
1257     {
1258         result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
1259         goto error;
1260     }
1261 
1262     deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
1263 
1264 #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1265     if( deviceCount > 0 )
1266     {
1267         deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
1268         FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
1269     }
1270 #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1271 
1272     if( deviceCount > 0 )
1273     {
1274         /* allocate array for pointers to PaDeviceInfo structs */
1275         (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1276                 winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
1277         if( !(*hostApi)->deviceInfos )
1278         {
1279             result = paInsufficientMemory;
1280             goto error;
1281         }
1282 
1283         /* allocate all PaDeviceInfo structs in a contiguous block */
1284         deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
1285                 winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
1286         if( !deviceInfoArray )
1287         {
1288             result = paInsufficientMemory;
1289             goto error;
1290         }
1291 
1292         for( i=0; i < deviceCount; ++i )
1293         {
1294             PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
1295             deviceInfo->structVersion = 2;
1296             deviceInfo->hostApi = hostApiIndex;
1297             deviceInfo->name = 0;
1298             (*hostApi)->deviceInfos[i] = deviceInfo;
1299         }
1300 
1301         for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
1302         {
1303             result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
1304                     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
1305                     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
1306                     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
1307             if( result != paNoError )
1308                 goto error;
1309         }
1310 
1311         for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
1312         {
1313             result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
1314                     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
1315                     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
1316                     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
1317             if( result != paNoError )
1318                 goto error;
1319         }
1320     }
1321 
1322     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1323     if( result != paNoError )
1324         goto error;
1325 
1326     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1327     if( result != paNoError )
1328         goto error;
1329 
1330 
1331     (*hostApi)->Terminate = Terminate;
1332     (*hostApi)->OpenStream = OpenStream;
1333     (*hostApi)->IsFormatSupported = IsFormatSupported;
1334 
1335     PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
1336                                       StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1337                                       GetStreamTime, GetStreamCpuLoad,
1338                                       PaUtil_DummyRead, PaUtil_DummyWrite,
1339                                       PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1340 
1341     PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
1342                                       StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1343                                       GetStreamTime, PaUtil_DummyGetCpuLoad,
1344                                       ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1345 
1346     return result;
1347 
1348 error:
1349     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1350     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1351 
1352     Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
1353 
1354     return result;
1355 }
1356 
1357 
1358 /***********************************************************************************/
Terminate(struct PaUtilHostApiRepresentation * hostApi)1359 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1360 {
1361     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1362 
1363     if( winDsHostApi ){
1364         if( winDsHostApi->allocations )
1365         {
1366             PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1367             PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1368         }
1369 
1370         PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
1371 
1372         PaUtil_FreeMemory( winDsHostApi );
1373     }
1374 
1375     PaWinDs_TerminateDSoundEntryPoints();
1376 }
1377 
ValidateWinDirectSoundSpecificStreamInfo(const PaStreamParameters * streamParameters,const PaWinDirectSoundStreamInfo * streamInfo)1378 static PaError ValidateWinDirectSoundSpecificStreamInfo(
1379         const PaStreamParameters *streamParameters,
1380         const PaWinDirectSoundStreamInfo *streamInfo )
1381 {
1382     if( streamInfo )
1383     {
1384         if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
1385                 || streamInfo->version != 2 )
1386         {
1387             return paIncompatibleHostApiSpecificStreamInfo;
1388         }
1389 
1390         if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1391         {
1392             if( streamInfo->framesPerBuffer <= 0 )
1393                 return paIncompatibleHostApiSpecificStreamInfo;
1394 
1395         }
1396     }
1397 
1398     return paNoError;
1399 }
1400 
1401 /***********************************************************************************/
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)1402 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1403                                   const PaStreamParameters *inputParameters,
1404                                   const PaStreamParameters *outputParameters,
1405                                   double sampleRate )
1406 {
1407     PaError result;
1408     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1409     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1410     int inputChannelCount, outputChannelCount;
1411     PaSampleFormat inputSampleFormat, outputSampleFormat;
1412     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1413 
1414     if( inputParameters )
1415     {
1416         inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1417         inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1418 
1419         inputChannelCount = inputParameters->channelCount;
1420         inputSampleFormat = inputParameters->sampleFormat;
1421 
1422         /* unless alternate device specification is supported, reject the use of
1423             paUseHostApiSpecificDeviceSpecification */
1424 
1425         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1426             return paInvalidDevice;
1427 
1428         /* check that input device can support inputChannelCount */
1429         if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1430                 && inputChannelCount > inputDeviceInfo->maxInputChannels )
1431             return paInvalidChannelCount;
1432 
1433         /* validate inputStreamInfo */
1434         inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1435         result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1436         if( result != paNoError ) return result;
1437     }
1438     else
1439     {
1440         inputChannelCount = 0;
1441     }
1442 
1443     if( outputParameters )
1444     {
1445         outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1446         outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1447 
1448         outputChannelCount = outputParameters->channelCount;
1449         outputSampleFormat = outputParameters->sampleFormat;
1450 
1451         /* unless alternate device specification is supported, reject the use of
1452             paUseHostApiSpecificDeviceSpecification */
1453 
1454         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1455             return paInvalidDevice;
1456 
1457         /* check that output device can support inputChannelCount */
1458         if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1459                 && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1460             return paInvalidChannelCount;
1461 
1462         /* validate outputStreamInfo */
1463         outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1464         result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1465         if( result != paNoError ) return result;
1466     }
1467     else
1468     {
1469         outputChannelCount = 0;
1470     }
1471 
1472     /*
1473         IMPLEMENT ME:
1474 
1475             - if a full duplex stream is requested, check that the combination
1476                 of input and output parameters is supported if necessary
1477 
1478             - check that the device supports sampleRate
1479 
1480         Because the buffer adapter handles conversion between all standard
1481         sample formats, the following checks are only required if paCustomFormat
1482         is implemented, or under some other unusual conditions.
1483 
1484             - check that input device can support inputSampleFormat, or that
1485                 we have the capability to convert from outputSampleFormat to
1486                 a native format
1487 
1488             - check that output device can support outputSampleFormat, or that
1489                 we have the capability to convert from outputSampleFormat to
1490                 a native format
1491     */
1492 
1493     return paFormatIsSupported;
1494 }
1495 
1496 
1497 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
InitFullDuplexInputOutputBuffers(PaWinDsStream * stream,PaWinDsDeviceInfo * inputDevice,PaSampleFormat hostInputSampleFormat,WORD inputChannelCount,int bytesPerInputBuffer,PaWinWaveFormatChannelMask inputChannelMask,PaWinDsDeviceInfo * outputDevice,PaSampleFormat hostOutputSampleFormat,WORD outputChannelCount,int bytesPerOutputBuffer,PaWinWaveFormatChannelMask outputChannelMask,unsigned long nFrameRate)1498 static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
1499                                        PaWinDsDeviceInfo *inputDevice,
1500                                        PaSampleFormat hostInputSampleFormat,
1501                                        WORD inputChannelCount,
1502                                        int bytesPerInputBuffer,
1503                                        PaWinWaveFormatChannelMask inputChannelMask,
1504                                        PaWinDsDeviceInfo *outputDevice,
1505                                        PaSampleFormat hostOutputSampleFormat,
1506                                        WORD outputChannelCount,
1507                                        int bytesPerOutputBuffer,
1508                                        PaWinWaveFormatChannelMask outputChannelMask,
1509                                        unsigned long nFrameRate
1510                                         )
1511 {
1512     HRESULT hr;
1513     DSCBUFFERDESC  captureDesc;
1514     PaWinWaveFormat captureWaveFormat;
1515     DSBUFFERDESC   secondaryRenderDesc;
1516     PaWinWaveFormat renderWaveFormat;
1517     LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
1518     LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
1519 
1520     // capture buffer description
1521 
1522     // only try wave format extensible. assume it's available on all ds 8 systems
1523     PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
1524                 hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
1525                 nFrameRate, inputChannelMask );
1526 
1527     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1528     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1529     captureDesc.dwFlags = 0;
1530     captureDesc.dwBufferBytes = bytesPerInputBuffer;
1531     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
1532 
1533     // render buffer description
1534 
1535     PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
1536                 hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
1537                 nFrameRate, outputChannelMask );
1538 
1539     ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
1540     secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
1541     secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1542     secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
1543     secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
1544 
1545     /* note that we don't create a primary buffer here at all */
1546 
1547     hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
1548             inputDevice->lpGUID, outputDevice->lpGUID,
1549             &captureDesc, &secondaryRenderDesc,
1550             GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
1551             DSSCL_EXCLUSIVE,
1552             &stream->pDirectSoundFullDuplex8,
1553             &pCaptureBuffer8,
1554             &pRenderBuffer8,
1555             NULL /* pUnkOuter must be NULL */
1556         );
1557 
1558     if( hr == DS_OK )
1559     {
1560         PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
1561 
1562         /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
1563 
1564         hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
1565 
1566         if( hr == DS_OK )
1567             hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
1568 
1569         /* release the ds 8 interfaces, we don't need them */
1570         IUnknown_Release( pCaptureBuffer8 );
1571         IUnknown_Release( pRenderBuffer8 );
1572 
1573         if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
1574             /* couldn't get pre ds 8 interfaces for some reason. clean up. */
1575             if( stream->pDirectSoundInputBuffer )
1576             {
1577                 IUnknown_Release( stream->pDirectSoundInputBuffer );
1578                 stream->pDirectSoundInputBuffer = NULL;
1579             }
1580 
1581             if( stream->pDirectSoundOutputBuffer )
1582             {
1583                 IUnknown_Release( stream->pDirectSoundOutputBuffer );
1584                 stream->pDirectSoundOutputBuffer = NULL;
1585             }
1586 
1587             IUnknown_Release( stream->pDirectSoundFullDuplex8 );
1588             stream->pDirectSoundFullDuplex8 = NULL;
1589         }
1590     }
1591     else
1592     {
1593         PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
1594     }
1595 
1596     return hr;
1597 }
1598 #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
1599 
1600 
InitInputBuffer(PaWinDsStream * stream,PaWinDsDeviceInfo * device,PaSampleFormat sampleFormat,unsigned long nFrameRate,WORD nChannels,int bytesPerBuffer,PaWinWaveFormatChannelMask channelMask)1601 static HRESULT InitInputBuffer( PaWinDsStream *stream,
1602                                PaWinDsDeviceInfo *device,
1603                                PaSampleFormat sampleFormat,
1604                                unsigned long nFrameRate,
1605                                WORD nChannels,
1606                                int bytesPerBuffer,
1607                                PaWinWaveFormatChannelMask channelMask )
1608 {
1609     DSCBUFFERDESC  captureDesc;
1610     PaWinWaveFormat waveFormat;
1611     HRESULT        result;
1612 
1613     if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
1614             device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
1615          ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
1616          return result;
1617     }
1618 
1619     // Setup the secondary buffer description
1620     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1621     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1622     captureDesc.dwFlags = 0;
1623     captureDesc.dwBufferBytes = bytesPerBuffer;
1624     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
1625 
1626     // Create the capture buffer
1627 
1628     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1629     PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1630                 sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1631                 nFrameRate, channelMask );
1632 
1633     if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1634                   &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
1635     {
1636         PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1637                 PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1638 
1639         if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1640                     &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
1641     }
1642 
1643     stream->readOffset = 0;  // reset last read position to start of buffer
1644     return DS_OK;
1645 }
1646 
1647 
InitOutputBuffer(PaWinDsStream * stream,PaWinDsDeviceInfo * device,PaSampleFormat sampleFormat,unsigned long nFrameRate,WORD nChannels,int bytesPerBuffer,PaWinWaveFormatChannelMask channelMask)1648 static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
1649                                 PaSampleFormat sampleFormat, unsigned long nFrameRate,
1650                                 WORD nChannels, int bytesPerBuffer,
1651                                 PaWinWaveFormatChannelMask channelMask )
1652 {
1653     HRESULT        result;
1654     HWND           hWnd;
1655     HRESULT        hr;
1656     PaWinWaveFormat waveFormat;
1657     DSBUFFERDESC   primaryDesc;
1658     DSBUFFERDESC   secondaryDesc;
1659 
1660     if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
1661                 device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
1662         ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
1663         return hr;
1664     }
1665 
1666     // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
1667     // applications's window. Also if that window is closed before the Buffer is closed
1668     // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
1669     // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
1670     // hWnd = GetForegroundWindow();
1671     //
1672     //  FIXME: The example code I have on the net creates a hidden window that
1673     //      is managed by our code - I think we should do that - one hidden
1674     //      window for the whole of Pa_DS
1675     //
1676     hWnd = GetDesktopWindow();
1677 
1678     // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
1679     // exclusive also prevents unexpected sounds from other apps during a performance.
1680     if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
1681               hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
1682     {
1683         return hr;
1684     }
1685 
1686     // -----------------------------------------------------------------------
1687     // Create primary buffer and set format just so we can specify our custom format.
1688     // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
1689     // Setup the primary buffer description
1690     ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
1691     primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
1692     primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
1693     primaryDesc.dwBufferBytes = 0;
1694     primaryDesc.lpwfxFormat   = NULL;
1695     // Create the buffer
1696     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1697                   &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
1698         goto error;
1699 
1700     // Set the primary buffer's format
1701 
1702     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1703     PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1704                 sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1705                 nFrameRate, channelMask );
1706 
1707     if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
1708     {
1709         PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1710                 PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1711 
1712         if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
1713             goto error;
1714     }
1715 
1716     // ----------------------------------------------------------------------
1717     // Setup the secondary buffer description
1718     ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
1719     secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
1720     secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1721     secondaryDesc.dwBufferBytes = bytesPerBuffer;
1722     secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
1723     // Create the secondary buffer
1724     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1725                   &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
1726       goto error;
1727 
1728     return DS_OK;
1729 
1730 error:
1731 
1732     if( stream->pDirectSoundPrimaryBuffer )
1733     {
1734         IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
1735         stream->pDirectSoundPrimaryBuffer = NULL;
1736     }
1737 
1738     return result;
1739 }
1740 
1741 
CalculateBufferSettings(unsigned long * hostBufferSizeFrames,unsigned long * pollingPeriodFrames,int isFullDuplex,unsigned long suggestedInputLatencyFrames,unsigned long suggestedOutputLatencyFrames,double sampleRate,unsigned long userFramesPerBuffer)1742 static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
1743                                     unsigned long *pollingPeriodFrames,
1744                                     int isFullDuplex,
1745                                     unsigned long suggestedInputLatencyFrames,
1746                                     unsigned long suggestedOutputLatencyFrames,
1747                                     double sampleRate, unsigned long userFramesPerBuffer )
1748 {
1749     unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
1750     unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
1751     unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
1752 
1753     if( userFramesPerBuffer == paFramesPerBufferUnspecified )
1754     {
1755         unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
1756 
1757         *pollingPeriodFrames = targetBufferingLatencyFrames / 4;
1758         if( *pollingPeriodFrames < minimumPollingPeriodFrames )
1759         {
1760             *pollingPeriodFrames = minimumPollingPeriodFrames;
1761         }
1762         else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1763         {
1764             *pollingPeriodFrames = maximumPollingPeriodFrames;
1765         }
1766 
1767         *hostBufferSizeFrames = *pollingPeriodFrames
1768                 + max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
1769     }
1770     else
1771     {
1772         unsigned long targetBufferingLatencyFrames = suggestedInputLatencyFrames;
1773         if( isFullDuplex )
1774         {
1775             /* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
1776                extra fixed latency. so we subtract it here as a fixed latency before computing
1777                the buffer size. being careful not to produce an unrepresentable negative result.
1778 
1779                Note: this only works as expected if output latency is greater than input latency.
1780                Otherwise we use input latency anyway since we do max(in,out).
1781             */
1782 
1783             if( userFramesPerBuffer < suggestedOutputLatencyFrames )
1784             {
1785                 unsigned long adjustedSuggestedOutputLatencyFrames =
1786                         suggestedOutputLatencyFrames - userFramesPerBuffer;
1787 
1788                 /* maximum of input and adjusted output suggested latency */
1789                 if( adjustedSuggestedOutputLatencyFrames > targetBufferingLatencyFrames )
1790                     targetBufferingLatencyFrames = adjustedSuggestedOutputLatencyFrames;
1791             }
1792         }
1793         else
1794         {
1795             /* maximum of input and output suggested latency */
1796             if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
1797                 targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
1798         }
1799 
1800         *hostBufferSizeFrames = userFramesPerBuffer
1801                 + max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
1802 
1803         *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
1804 
1805         if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1806         {
1807             *pollingPeriodFrames = maximumPollingPeriodFrames;
1808         }
1809     }
1810 }
1811 
1812 
CalculatePollingPeriodFrames(unsigned long hostBufferSizeFrames,unsigned long * pollingPeriodFrames,double sampleRate,unsigned long userFramesPerBuffer)1813 static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
1814                                     unsigned long *pollingPeriodFrames,
1815                                     double sampleRate, unsigned long userFramesPerBuffer )
1816 {
1817     unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
1818     unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
1819     unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
1820 
1821     *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );
1822 
1823     if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1824     {
1825         *pollingPeriodFrames = maximumPollingPeriodFrames;
1826     }
1827 }
1828 
1829 
SetStreamInfoLatencies(PaWinDsStream * stream,unsigned long userFramesPerBuffer,unsigned long pollingPeriodFrames,double sampleRate)1830 static void SetStreamInfoLatencies( PaWinDsStream *stream,
1831                                    unsigned long userFramesPerBuffer,
1832                                    unsigned long pollingPeriodFrames,
1833                                    double sampleRate )
1834 {
1835     /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
1836     and the configuration of the buffer processor */
1837 
1838     unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
1839                                              ? pollingPeriodFrames
1840                                              : userFramesPerBuffer;
1841 
1842     if( stream->bufferProcessor.inputChannelCount > 0 )
1843     {
1844         /* stream info input latency is the minimum buffering latency
1845            (unlike suggested and default which are *maximums*) */
1846         stream->streamRepresentation.streamInfo.inputLatency =
1847                 (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
1848                     + effectiveFramesPerBuffer) / sampleRate;
1849     }
1850     else
1851     {
1852         stream->streamRepresentation.streamInfo.inputLatency = 0;
1853     }
1854 
1855     if( stream->bufferProcessor.outputChannelCount > 0 )
1856     {
1857         stream->streamRepresentation.streamInfo.outputLatency =
1858                 (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
1859                     + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
1860     }
1861     else
1862     {
1863         stream->streamRepresentation.streamInfo.outputLatency = 0;
1864     }
1865 }
1866 
1867 
1868 /***********************************************************************************/
1869 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1870 
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long framesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)1871 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1872                            PaStream** s,
1873                            const PaStreamParameters *inputParameters,
1874                            const PaStreamParameters *outputParameters,
1875                            double sampleRate,
1876                            unsigned long framesPerBuffer,
1877                            PaStreamFlags streamFlags,
1878                            PaStreamCallback *streamCallback,
1879                            void *userData )
1880 {
1881     PaError result = paNoError;
1882     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1883     PaWinDsStream *stream = 0;
1884     int bufferProcessorIsInitialized = 0;
1885     int streamRepresentationIsInitialized = 0;
1886     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1887     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1888     int inputChannelCount, outputChannelCount;
1889     PaSampleFormat inputSampleFormat, outputSampleFormat;
1890     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1891     int userRequestedHostInputBufferSizeFrames = 0;
1892     int userRequestedHostOutputBufferSizeFrames = 0;
1893     unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
1894     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1895     PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
1896     unsigned long pollingPeriodFrames = 0;
1897 
1898     if( inputParameters )
1899     {
1900         inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1901         inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1902 
1903         inputChannelCount = inputParameters->channelCount;
1904         inputSampleFormat = inputParameters->sampleFormat;
1905         suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
1906 
1907         /* IDEA: the following 3 checks could be performed by default by pa_front
1908             unless some flag indicated otherwise */
1909 
1910         /* unless alternate device specification is supported, reject the use of
1911             paUseHostApiSpecificDeviceSpecification */
1912         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1913             return paInvalidDevice;
1914 
1915         /* check that input device can support inputChannelCount */
1916         if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1917                 && inputChannelCount > inputDeviceInfo->maxInputChannels )
1918             return paInvalidChannelCount;
1919 
1920         /* validate hostApiSpecificStreamInfo */
1921         inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1922         result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1923         if( result != paNoError ) return result;
1924 
1925         if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1926             userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;
1927 
1928         if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1929             inputChannelMask = inputStreamInfo->channelMask;
1930         else
1931             inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
1932     }
1933     else
1934     {
1935         inputChannelCount = 0;
1936         inputSampleFormat = 0;
1937         suggestedInputLatencyFrames = 0;
1938     }
1939 
1940 
1941     if( outputParameters )
1942     {
1943         outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1944         outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1945 
1946         outputChannelCount = outputParameters->channelCount;
1947         outputSampleFormat = outputParameters->sampleFormat;
1948         suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
1949 
1950         /* unless alternate device specification is supported, reject the use of
1951             paUseHostApiSpecificDeviceSpecification */
1952         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1953             return paInvalidDevice;
1954 
1955         /* check that output device can support outputChannelCount */
1956         if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1957                 && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1958             return paInvalidChannelCount;
1959 
1960         /* validate hostApiSpecificStreamInfo */
1961         outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1962         result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1963         if( result != paNoError ) return result;
1964 
1965         if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1966             userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
1967 
1968         if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1969             outputChannelMask = outputStreamInfo->channelMask;
1970         else
1971             outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
1972     }
1973     else
1974     {
1975         outputChannelCount = 0;
1976         outputSampleFormat = 0;
1977         suggestedOutputLatencyFrames = 0;
1978     }
1979 
1980     /*
1981         If low level host buffer size is specified for both input and output
1982         the current code requires the sizes to match.
1983     */
1984 
1985     if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
1986             && userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
1987         return paIncompatibleHostApiSpecificStreamInfo;
1988 
1989 
1990 
1991     /*
1992         IMPLEMENT ME:
1993 
1994         ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
1995 
1996             - check that input device can support inputSampleFormat, or that
1997                 we have the capability to convert from outputSampleFormat to
1998                 a native format
1999 
2000             - check that output device can support outputSampleFormat, or that
2001                 we have the capability to convert from outputSampleFormat to
2002                 a native format
2003 
2004             - if a full duplex stream is requested, check that the combination
2005                 of input and output parameters is supported
2006 
2007             - check that the device supports sampleRate
2008 
2009             - alter sampleRate to a close allowable rate if possible / necessary
2010 
2011             - validate suggestedInputLatency and suggestedOutputLatency parameters,
2012                 use default values where necessary
2013     */
2014 
2015 
2016     /* validate platform specific flags */
2017     if( (streamFlags & paPlatformSpecificFlags) != 0 )
2018         return paInvalidFlag; /* unexpected platform specific flag */
2019 
2020 
2021     stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
2022     if( !stream )
2023     {
2024         result = paInsufficientMemory;
2025         goto error;
2026     }
2027 
2028     memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
2029 
2030     if( streamCallback )
2031     {
2032         PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2033                                                &winDsHostApi->callbackStreamInterface, streamCallback, userData );
2034     }
2035     else
2036     {
2037         PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2038                                                &winDsHostApi->blockingStreamInterface, streamCallback, userData );
2039     }
2040 
2041     streamRepresentationIsInitialized = 1;
2042 
2043     stream->streamFlags = streamFlags;
2044 
2045     PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2046 
2047 
2048     if( inputParameters )
2049     {
2050         /* IMPLEMENT ME - establish which  host formats are available */
2051         PaSampleFormat nativeInputFormats = paInt16;
2052         /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
2053 
2054         hostInputSampleFormat =
2055             PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
2056     }
2057     else
2058     {
2059         hostInputSampleFormat = 0;
2060     }
2061 
2062     if( outputParameters )
2063     {
2064         /* IMPLEMENT ME - establish which  host formats are available */
2065         PaSampleFormat nativeOutputFormats = paInt16;
2066         /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
2067 
2068         hostOutputSampleFormat =
2069             PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
2070     }
2071     else
2072     {
2073         hostOutputSampleFormat = 0;
2074     }
2075 
2076     result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2077                     inputChannelCount, inputSampleFormat, hostInputSampleFormat,
2078                     outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
2079                     sampleRate, streamFlags, framesPerBuffer,
2080                     0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
2081                 /* This next mode is required because DS can split the host buffer when it wraps around. */
2082                     paUtilVariableHostBufferSizePartialUsageAllowed,
2083                     streamCallback, userData );
2084     if( result != paNoError )
2085         goto error;
2086 
2087     bufferProcessorIsInitialized = 1;
2088 
2089 
2090 /* DirectSound specific initialization */
2091     {
2092         HRESULT          hr;
2093         unsigned long    integerSampleRate = (unsigned long) (sampleRate + 0.5);
2094 
2095         stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
2096         if( stream->processingCompleted == NULL )
2097         {
2098             result = paInsufficientMemory;
2099             goto error;
2100         }
2101 
2102 #ifdef PA_WIN_DS_USE_WMME_TIMER
2103         stream->timerID = 0;
2104 #endif
2105 
2106 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2107         stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
2108         if( stream->waitableTimer == NULL )
2109         {
2110             result = paUnanticipatedHostError;
2111             PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
2112             goto error;
2113         }
2114 #endif
2115 
2116 #ifndef PA_WIN_DS_USE_WMME_TIMER
2117         stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
2118         if( stream->processingThreadCompleted == NULL )
2119         {
2120             result = paUnanticipatedHostError;
2121             PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
2122             goto error;
2123         }
2124 #endif
2125 
2126         /* set up i/o parameters */
2127 
2128         if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
2129         {
2130             /* use low level parameters */
2131 
2132             /* since we use the same host buffer size for input and output
2133                we choose the highest user specified value.
2134             */
2135             stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
2136 
2137             CalculatePollingPeriodFrames(
2138                     stream->hostBufferSizeFrames, &pollingPeriodFrames,
2139                     sampleRate, framesPerBuffer );
2140         }
2141         else
2142         {
2143             CalculateBufferSettings( (unsigned long*)&stream->hostBufferSizeFrames, &pollingPeriodFrames,
2144                     /* isFullDuplex = */ (inputParameters && outputParameters),
2145                     suggestedInputLatencyFrames,
2146                     suggestedOutputLatencyFrames,
2147                     sampleRate, framesPerBuffer );
2148         }
2149 
2150         stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
2151 
2152         DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
2153                 stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
2154 
2155 
2156         /* ------------------ OUTPUT */
2157         if( outputParameters )
2158         {
2159             LARGE_INTEGER  counterFrequency;
2160 
2161             /*
2162             PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
2163             DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
2164             */
2165 
2166             int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
2167             stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
2168 
2169             stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
2170             if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
2171             {
2172                 result = paBufferTooSmall;
2173                 goto error;
2174             }
2175             else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
2176             {
2177                 result = paBufferTooBig;
2178                 goto error;
2179             }
2180 
2181             /* Calculate value used in latency calculation to avoid real-time divides. */
2182             stream->secondsPerHostByte = 1.0 /
2183                 (stream->bufferProcessor.bytesPerHostOutputSample *
2184                 outputChannelCount * sampleRate);
2185 
2186             stream->outputIsRunning = FALSE;
2187             stream->outputUnderflowCount = 0;
2188 
2189             /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
2190             if( QueryPerformanceFrequency( &counterFrequency ) )
2191             {
2192                 stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
2193             }
2194             else
2195             {
2196                 stream->perfCounterTicksPerBuffer.QuadPart = 0;
2197             }
2198         }
2199 
2200         /* ------------------ INPUT */
2201         if( inputParameters )
2202         {
2203             /*
2204             PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
2205             DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
2206             */
2207 
2208             int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
2209             stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
2210 
2211             stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
2212             if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
2213             {
2214                 result = paBufferTooSmall;
2215                 goto error;
2216             }
2217             else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
2218             {
2219                 result = paBufferTooBig;
2220                 goto error;
2221             }
2222         }
2223 
2224         /* open/create the DirectSound buffers */
2225 
2226         /* interface ptrs should be zeroed when stream is zeroed. */
2227         assert( stream->pDirectSoundCapture == NULL );
2228         assert( stream->pDirectSoundInputBuffer == NULL );
2229         assert( stream->pDirectSound == NULL );
2230         assert( stream->pDirectSoundPrimaryBuffer == NULL );
2231         assert( stream->pDirectSoundOutputBuffer == NULL );
2232 
2233 
2234         if( inputParameters && outputParameters )
2235         {
2236 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2237             /* try to use the full-duplex DX8 API to create the buffers.
2238                 if that fails we fall back to the half-duplex API below */
2239 
2240             hr = InitFullDuplexInputOutputBuffers( stream,
2241                                        (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2242                                        hostInputSampleFormat,
2243                                        (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
2244                                        inputChannelMask,
2245                                        (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2246                                        hostOutputSampleFormat,
2247                                        (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
2248                                        outputChannelMask,
2249                                        integerSampleRate
2250                                         );
2251             DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
2252             /* ignore any error returned by InitFullDuplexInputOutputBuffers.
2253                 we retry opening the buffers below */
2254 #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
2255         }
2256 
2257         /*  create half duplex buffers. also used for full-duplex streams which didn't
2258             succeed when using the full duplex API. that could happen because
2259             DX8 or greater isnt installed, the i/o devices aren't the same
2260             physical device. etc.
2261         */
2262 
2263         if( outputParameters && !stream->pDirectSoundOutputBuffer )
2264         {
2265             hr = InitOutputBuffer( stream,
2266                                        (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2267                                        hostOutputSampleFormat,
2268                                        integerSampleRate,
2269                                        (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
2270                                        outputChannelMask );
2271             DBUG(("InitOutputBuffer() returns %x\n", hr));
2272             if( hr != DS_OK )
2273             {
2274                 result = paUnanticipatedHostError;
2275                 PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2276                 goto error;
2277             }
2278         }
2279 
2280         if( inputParameters && !stream->pDirectSoundInputBuffer )
2281         {
2282             hr = InitInputBuffer( stream,
2283                                       (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2284                                       hostInputSampleFormat,
2285                                       integerSampleRate,
2286                                       (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
2287                                       inputChannelMask );
2288             DBUG(("InitInputBuffer() returns %x\n", hr));
2289             if( hr != DS_OK )
2290             {
2291                 ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
2292                 result = paUnanticipatedHostError;
2293                 PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2294                 goto error;
2295             }
2296         }
2297     }
2298 
2299     SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
2300 
2301     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2302 
2303     *s = (PaStream*)stream;
2304 
2305     return result;
2306 
2307 error:
2308     if( stream )
2309     {
2310         if( stream->processingCompleted != NULL )
2311             CloseHandle( stream->processingCompleted );
2312 
2313 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2314         if( stream->waitableTimer != NULL )
2315             CloseHandle( stream->waitableTimer );
2316 #endif
2317 
2318 #ifndef PA_WIN_DS_USE_WMME_TIMER
2319         if( stream->processingThreadCompleted != NULL )
2320             CloseHandle( stream->processingThreadCompleted );
2321 #endif
2322 
2323         if( stream->pDirectSoundOutputBuffer )
2324         {
2325             IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2326             IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2327             stream->pDirectSoundOutputBuffer = NULL;
2328         }
2329 
2330         if( stream->pDirectSoundPrimaryBuffer )
2331         {
2332             IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2333             stream->pDirectSoundPrimaryBuffer = NULL;
2334         }
2335 
2336         if( stream->pDirectSoundInputBuffer )
2337         {
2338             IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2339             IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2340             stream->pDirectSoundInputBuffer = NULL;
2341         }
2342 
2343         if( stream->pDirectSoundCapture )
2344         {
2345             IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2346             stream->pDirectSoundCapture = NULL;
2347         }
2348 
2349         if( stream->pDirectSound )
2350         {
2351             IDirectSound_Release( stream->pDirectSound );
2352             stream->pDirectSound = NULL;
2353         }
2354 
2355 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2356         if( stream->pDirectSoundFullDuplex8 )
2357         {
2358             IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2359             stream->pDirectSoundFullDuplex8 = NULL;
2360         }
2361 #endif
2362         if( bufferProcessorIsInitialized )
2363             PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2364 
2365         if( streamRepresentationIsInitialized )
2366             PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2367 
2368         PaUtil_FreeMemory( stream );
2369     }
2370 
2371     return result;
2372 }
2373 
2374 
2375 /************************************************************************************
2376  * Determine how much space can be safely written to in DS buffer.
2377  * Detect underflows and overflows.
2378  * Does not allow writing into safety gap maintained by DirectSound.
2379  */
QueryOutputSpace(PaWinDsStream * stream,long * bytesEmpty)2380 static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
2381 {
2382     HRESULT hr;
2383     DWORD   playCursor;
2384     DWORD   writeCursor;
2385     long    numBytesEmpty;
2386     long    playWriteGap;
2387     // Query to see how much room is in buffer.
2388     hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2389             &playCursor, &writeCursor );
2390     if( hr != DS_OK )
2391     {
2392         return hr;
2393     }
2394 
2395     // Determine size of gap between playIndex and WriteIndex that we cannot write into.
2396     playWriteGap = writeCursor - playCursor;
2397     if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
2398 
2399     /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
2400     /* Attempt to detect playCursor wrap-around and correct it. */
2401     if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
2402     {
2403         /* How much time has elapsed since last check. */
2404         LARGE_INTEGER   currentTime;
2405         LARGE_INTEGER   elapsedTime;
2406         long            bytesPlayed;
2407         long            bytesExpected;
2408         long            buffersWrapped;
2409 
2410         QueryPerformanceCounter( &currentTime );
2411         elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
2412         stream->previousPlayTime = currentTime;
2413 
2414         /* How many bytes does DirectSound say have been played. */
2415         bytesPlayed = playCursor - stream->previousPlayCursor;
2416         if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
2417         stream->previousPlayCursor = playCursor;
2418 
2419         /* Calculate how many bytes we would have expected to been played by now. */
2420         bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
2421         buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
2422         if( buffersWrapped > 0 )
2423         {
2424             playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
2425             bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
2426         }
2427     }
2428     numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
2429     if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
2430 
2431     /* Have we underflowed? */
2432     if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
2433     {
2434         if( stream->outputIsRunning )
2435         {
2436             stream->outputUnderflowCount += 1;
2437         }
2438 
2439         /*
2440             From MSDN:
2441                 The write cursor indicates the position at which it is safe
2442             to write new data to the buffer. The write cursor always leads the
2443             play cursor, typically by about 15 milliseconds' worth of audio
2444             data.
2445                 It is always safe to change data that is behind the position
2446             indicated by the lpdwCurrentPlayCursor parameter.
2447         */
2448 
2449         stream->outputBufferWriteOffsetBytes = writeCursor;
2450         numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
2451     }
2452     *bytesEmpty = numBytesEmpty;
2453     return hr;
2454 }
2455 
2456 /***********************************************************************************/
TimeSlice(PaWinDsStream * stream)2457 static int TimeSlice( PaWinDsStream *stream )
2458 {
2459     long              numFrames = 0;
2460     long              bytesEmpty = 0;
2461     long              bytesFilled = 0;
2462     long              bytesToXfer = 0;
2463     long              framesToXfer = 0; /* the number of frames we'll process this tick */
2464     long              numInFramesReady = 0;
2465     long              numOutFramesReady = 0;
2466     long              bytesProcessed;
2467     HRESULT           hresult;
2468     double            outputLatency = 0;
2469     double            inputLatency = 0;
2470     PaStreamCallbackTimeInfo timeInfo = {0,0,0};
2471 
2472 /* Input */
2473     LPBYTE            lpInBuf1 = NULL;
2474     LPBYTE            lpInBuf2 = NULL;
2475     DWORD             dwInSize1 = 0;
2476     DWORD             dwInSize2 = 0;
2477 /* Output */
2478     LPBYTE            lpOutBuf1 = NULL;
2479     LPBYTE            lpOutBuf2 = NULL;
2480     DWORD             dwOutSize1 = 0;
2481     DWORD             dwOutSize2 = 0;
2482 
2483     /* How much input data is available? */
2484     if( stream->bufferProcessor.inputChannelCount > 0 )
2485     {
2486         HRESULT hr;
2487         DWORD capturePos;
2488         DWORD readPos;
2489         long  filled = 0;
2490         // Query to see how much data is in buffer.
2491         // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
2492         // so let's pass a pointer just to be safe.
2493         hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
2494         if( hr == DS_OK )
2495         {
2496             filled = readPos - stream->readOffset;
2497             if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
2498             bytesFilled = filled;
2499 
2500             inputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
2501         }
2502             // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
2503 
2504         framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
2505 
2506         /** @todo Check for overflow */
2507     }
2508 
2509     /* How much output room is available? */
2510     if( stream->bufferProcessor.outputChannelCount > 0 )
2511     {
2512         UINT previousUnderflowCount = stream->outputUnderflowCount;
2513         QueryOutputSpace( stream, &bytesEmpty );
2514         framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
2515 
2516         /* Check for underflow */
2517         /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
2518             A query function should be a const operator on the stream and return a flag on underflow. */
2519         if( stream->outputUnderflowCount != previousUnderflowCount )
2520             stream->callbackFlags |= paOutputUnderflow;
2521 
2522         /* We are about to compute audio into the first byte of empty space in the output buffer.
2523            This audio will reach the DAC after all of the current (non-empty) audio
2524            in the buffer has played. Therefore the output time is the current time
2525            plus the time it takes to play the non-empty bytes in the buffer,
2526            computed here:
2527         */
2528         outputLatency = ((double)(stream->outputBufferSizeBytes - bytesEmpty)) * stream->secondsPerHostByte;
2529     }
2530 
2531     /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
2532     if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
2533     {
2534         framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
2535     }
2536 
2537     if( framesToXfer > 0 )
2538     {
2539         PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2540 
2541     /* The outputBufferDacTime parameter should indicates the time at which
2542         the first sample of the output buffer is heard at the DACs. */
2543         timeInfo.currentTime = PaUtil_GetTime();
2544 
2545         PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
2546         stream->callbackFlags = 0;
2547 
2548     /* Input */
2549         if( stream->bufferProcessor.inputChannelCount > 0 )
2550         {
2551             timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
2552 
2553             bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
2554             hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
2555                 stream->readOffset, bytesToXfer,
2556                 (void **) &lpInBuf1, &dwInSize1,
2557                 (void **) &lpInBuf2, &dwInSize2, 0);
2558             if (hresult != DS_OK)
2559             {
2560                 ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
2561                 /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2562                 PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2563                 stream->callbackResult = paComplete;
2564                 goto error2;
2565             }
2566 
2567             numFrames = dwInSize1 / stream->inputFrameSizeBytes;
2568             PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
2569             PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
2570         /* Is input split into two regions. */
2571             if( dwInSize2 > 0 )
2572             {
2573                 numFrames = dwInSize2 / stream->inputFrameSizeBytes;
2574                 PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
2575                 PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
2576             }
2577         }
2578 
2579     /* Output */
2580         if( stream->bufferProcessor.outputChannelCount > 0 )
2581         {
2582             /*
2583             We don't currently add outputLatency here because it appears to produce worse
2584             results than not adding it. Need to do more testing to verify this.
2585             */
2586             /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */
2587             timeInfo.outputBufferDacTime = timeInfo.currentTime;
2588 
2589             bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
2590             hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
2591                 stream->outputBufferWriteOffsetBytes, bytesToXfer,
2592                 (void **) &lpOutBuf1, &dwOutSize1,
2593                 (void **) &lpOutBuf2, &dwOutSize2, 0);
2594             if (hresult != DS_OK)
2595             {
2596                 ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
2597                 /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2598                 PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2599                 stream->callbackResult = paComplete;
2600                 goto error1;
2601             }
2602 
2603             numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
2604             PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
2605             PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
2606 
2607         /* Is output split into two regions. */
2608             if( dwOutSize2 > 0 )
2609             {
2610                 numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
2611                 PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
2612                 PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
2613             }
2614         }
2615 
2616         numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
2617         stream->framesWritten += numFrames;
2618 
2619         if( stream->bufferProcessor.outputChannelCount > 0 )
2620         {
2621         /* FIXME: an underflow could happen here */
2622 
2623         /* Update our buffer offset and unlock sound buffer */
2624             bytesProcessed = numFrames * stream->outputFrameSizeBytes;
2625             stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
2626             IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
2627         }
2628 
2629 error1:
2630         if( stream->bufferProcessor.inputChannelCount > 0 )
2631         {
2632         /* FIXME: an overflow could happen here */
2633 
2634         /* Update our buffer offset and unlock sound buffer */
2635             bytesProcessed = numFrames * stream->inputFrameSizeBytes;
2636             stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
2637             IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
2638         }
2639 error2:
2640 
2641         PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
2642     }
2643 
2644     if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2645     {
2646         /* don't return completed until the buffer processor has been drained */
2647         return paContinue;
2648     }
2649     else
2650     {
2651         return stream->callbackResult;
2652     }
2653 }
2654 /*******************************************************************/
2655 
ZeroAvailableOutputSpace(PaWinDsStream * stream)2656 static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
2657 {
2658     HRESULT hr;
2659     LPBYTE lpbuf1 = NULL;
2660     LPBYTE lpbuf2 = NULL;
2661     DWORD dwsize1 = 0;
2662     DWORD dwsize2 = 0;
2663     long  bytesEmpty;
2664     hr = QueryOutputSpace( stream, &bytesEmpty );
2665     if (hr != DS_OK) return hr;
2666     if( bytesEmpty == 0 ) return DS_OK;
2667     // Lock free space in the DS
2668     hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
2669                                     bytesEmpty, (void **) &lpbuf1, &dwsize1,
2670                                     (void **) &lpbuf2, &dwsize2, 0);
2671     if (hr == DS_OK)
2672     {
2673         // Copy the buffer into the DS
2674         ZeroMemory(lpbuf1, dwsize1);
2675         if(lpbuf2 != NULL)
2676         {
2677             ZeroMemory(lpbuf2, dwsize2);
2678         }
2679         // Update our buffer offset and unlock sound buffer
2680         stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
2681         IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
2682 
2683         stream->finalZeroBytesWritten += dwsize1 + dwsize2;
2684     }
2685     return hr;
2686 }
2687 
2688 
TimerCallback(UINT uID,UINT uMsg,DWORD_PTR dwUser,DWORD dw1,DWORD dw2)2689 static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
2690 {
2691     PaWinDsStream *stream;
2692     int isFinished = 0;
2693 
2694     /* suppress unused variable warnings */
2695     (void) uID;
2696     (void) uMsg;
2697     (void) dw1;
2698     (void) dw2;
2699 
2700     stream = (PaWinDsStream *) dwUser;
2701     if( stream == NULL ) return;
2702 
2703     if( stream->isActive )
2704     {
2705         if( stream->abortProcessing )
2706         {
2707             isFinished = 1;
2708         }
2709         else if( stream->stopProcessing )
2710         {
2711             if( stream->bufferProcessor.outputChannelCount > 0 )
2712             {
2713                 ZeroAvailableOutputSpace( stream );
2714                 if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
2715                 {
2716                     /* once we've flushed the whole output buffer with zeros we know all data has been played */
2717                     isFinished = 1;
2718                 }
2719             }
2720             else
2721             {
2722                 isFinished = 1;
2723             }
2724         }
2725         else
2726         {
2727             int callbackResult = TimeSlice( stream );
2728             if( callbackResult != paContinue )
2729             {
2730                 /* FIXME implement handling of paComplete and paAbort if possible
2731                    At the moment this should behave as if paComplete was called and
2732                    flush the buffer.
2733                 */
2734 
2735                 stream->stopProcessing = 1;
2736             }
2737         }
2738 
2739         if( isFinished )
2740         {
2741             if( stream->streamRepresentation.streamFinishedCallback != 0 )
2742                 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2743 
2744             stream->isActive = 0; /* don't set this until the stream really is inactive */
2745             SetEvent( stream->processingCompleted );
2746         }
2747     }
2748 }
2749 
2750 #ifndef PA_WIN_DS_USE_WMME_TIMER
2751 
2752 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2753 
WaitableTimerAPCProc(LPVOID lpArg,DWORD dwTimerLowValue,DWORD dwTimerHighValue)2754 static void CALLBACK WaitableTimerAPCProc(
2755    LPVOID lpArg,               // Data value
2756    DWORD dwTimerLowValue,      // Timer low value
2757    DWORD dwTimerHighValue )    // Timer high value
2758 
2759 {
2760     (void)dwTimerLowValue;
2761     (void)dwTimerHighValue;
2762 
2763     TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 );
2764 }
2765 
2766 #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
2767 
2768 
ProcessingThreadProc(void * pArg)2769 PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
2770 {
2771     PaWinDsStream *stream = (PaWinDsStream *)pArg;
2772     LARGE_INTEGER dueTime;
2773     int timerPeriodMs;
2774 
2775     timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
2776     if( timerPeriodMs < 1 )
2777         timerPeriodMs = 1;
2778 
2779 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2780     assert( stream->waitableTimer != NULL );
2781 
2782     /* invoke first timeout immediately */
2783     dueTime.LowPart = timerPeriodMs * 1000 * 10;
2784     dueTime.HighPart = 0;
2785 
2786     /* tick using waitable timer */
2787     if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
2788     {
2789         DWORD wfsoResult = 0;
2790         do
2791         {
2792             /* wait for processingCompleted to be signaled or our timer APC to be called */
2793             wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
2794 
2795         }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
2796     }
2797 
2798     CancelWaitableTimer( stream->waitableTimer );
2799 
2800 #else
2801 
2802     /* tick using WaitForSingleObject timout */
2803     while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
2804     {
2805         TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
2806     }
2807 #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
2808 
2809     SetEvent( stream->processingThreadCompleted );
2810 
2811     return 0;
2812 }
2813 
2814 #endif /* !PA_WIN_DS_USE_WMME_TIMER */
2815 
2816 /***********************************************************************************
2817     When CloseStream() is called, the multi-api layer ensures that
2818     the stream has already been stopped or aborted.
2819 */
CloseStream(PaStream * s)2820 static PaError CloseStream( PaStream* s )
2821 {
2822     PaError result = paNoError;
2823     PaWinDsStream *stream = (PaWinDsStream*)s;
2824 
2825     CloseHandle( stream->processingCompleted );
2826 
2827 #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2828     if( stream->waitableTimer != NULL )
2829         CloseHandle( stream->waitableTimer );
2830 #endif
2831 
2832 #ifndef PA_WIN_DS_USE_WMME_TIMER
2833     CloseHandle( stream->processingThreadCompleted );
2834 #endif
2835 
2836     // Cleanup the sound buffers
2837     if( stream->pDirectSoundOutputBuffer )
2838     {
2839         IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2840         IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2841         stream->pDirectSoundOutputBuffer = NULL;
2842     }
2843 
2844     if( stream->pDirectSoundPrimaryBuffer )
2845     {
2846         IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2847         stream->pDirectSoundPrimaryBuffer = NULL;
2848     }
2849 
2850     if( stream->pDirectSoundInputBuffer )
2851     {
2852         IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2853         IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2854         stream->pDirectSoundInputBuffer = NULL;
2855     }
2856 
2857     if( stream->pDirectSoundCapture )
2858     {
2859         IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2860         stream->pDirectSoundCapture = NULL;
2861     }
2862 
2863     if( stream->pDirectSound )
2864     {
2865         IDirectSound_Release( stream->pDirectSound );
2866         stream->pDirectSound = NULL;
2867     }
2868 
2869 #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2870     if( stream->pDirectSoundFullDuplex8 )
2871     {
2872         IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2873         stream->pDirectSoundFullDuplex8 = NULL;
2874     }
2875 #endif
2876 
2877     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2878     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2879     PaUtil_FreeMemory( stream );
2880 
2881     return result;
2882 }
2883 
2884 /***********************************************************************************/
ClearOutputBuffer(PaWinDsStream * stream)2885 static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
2886 {
2887     PaError          result = paNoError;
2888     unsigned char*   pDSBuffData;
2889     DWORD            dwDataLen;
2890     HRESULT          hr;
2891 
2892     hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
2893     DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
2894     if( hr != DS_OK )
2895         return hr;
2896 
2897     // Lock the DS buffer
2898     if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
2899                                            &dwDataLen, NULL, 0, 0)) != DS_OK )
2900         return hr;
2901 
2902     // Zero the DS buffer
2903     ZeroMemory(pDSBuffData, dwDataLen);
2904     // Unlock the DS buffer
2905     if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
2906         return hr;
2907 
2908     // Let DSound set the starting write position because if we set it to zero, it looks like the
2909     // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
2910     if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2911             &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
2912         return hr;
2913 
2914     /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
2915 
2916     return DS_OK;
2917 }
2918 
StartStream(PaStream * s)2919 static PaError StartStream( PaStream *s )
2920 {
2921     PaError          result = paNoError;
2922     PaWinDsStream   *stream = (PaWinDsStream*)s;
2923     HRESULT          hr;
2924 
2925     stream->callbackResult = paContinue;
2926     PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2927 
2928     ResetEvent( stream->processingCompleted );
2929 
2930 #ifndef PA_WIN_DS_USE_WMME_TIMER
2931     ResetEvent( stream->processingThreadCompleted );
2932 #endif
2933 
2934     if( stream->bufferProcessor.inputChannelCount > 0 )
2935     {
2936         // Start the buffer capture
2937         if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
2938         {
2939             hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
2940         }
2941 
2942         DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
2943         if( hr != DS_OK )
2944         {
2945             result = paUnanticipatedHostError;
2946             PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2947             goto error;
2948         }
2949     }
2950 
2951     stream->framesWritten = 0;
2952     stream->callbackFlags = 0;
2953 
2954     stream->abortProcessing = 0;
2955     stream->stopProcessing = 0;
2956 
2957     if( stream->bufferProcessor.outputChannelCount > 0 )
2958     {
2959         QueryPerformanceCounter( &stream->previousPlayTime );
2960         stream->finalZeroBytesWritten = 0;
2961 
2962         hr = ClearOutputBuffer( stream );
2963         if( hr != DS_OK )
2964         {
2965             result = paUnanticipatedHostError;
2966             PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2967             goto error;
2968         }
2969 
2970         if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
2971         {
2972             stream->callbackFlags = paPrimingOutput;
2973 
2974             TimeSlice( stream );
2975             /* we ignore the return value from TimeSlice here and start the stream as usual.
2976                 The first timer callback will detect if the callback has completed. */
2977 
2978             stream->callbackFlags = 0;
2979         }
2980 
2981         // Start the buffer playback in a loop.
2982         if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
2983         {
2984             hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
2985             DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
2986             if( hr != DS_OK )
2987             {
2988                 result = paUnanticipatedHostError;
2989                 PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2990                 goto error;
2991             }
2992             stream->outputIsRunning = TRUE;
2993         }
2994     }
2995 
2996     if( stream->streamRepresentation.streamCallback )
2997     {
2998         TIMECAPS timecaps;
2999         int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
3000         if( timerPeriodMs < 1 )
3001             timerPeriodMs = 1;
3002 
3003         /* set windows scheduler granularity only as fine as needed, no finer */
3004         /* Although this is not fully documented by MS, it appears that
3005            timeBeginPeriod() affects the scheduling granulatity of all timers
3006            including Waitable Timer Objects. So we always call timeBeginPeriod, whether
3007            we're using an MM timer callback via timeSetEvent or not.
3008         */
3009         assert( stream->systemTimerResolutionPeriodMs == 0 );
3010         if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) ) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 )
3011         {
3012             /* aim for resolution 4 times higher than polling rate */
3013             stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25);
3014             if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
3015                 stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
3016             if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
3017                 stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
3018 
3019             if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
3020                 stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
3021         }
3022 
3023 
3024 #ifdef PA_WIN_DS_USE_WMME_TIMER
3025         /* Create timer that will wake us up so we can fill the DSound buffer. */
3026         /* We have deprecated timeSetEvent because all MM timer callbacks
3027            are serialised onto a single thread. Which creates problems with multiple
3028            PA streams, or when also using timers for other time critical tasks
3029         */
3030         stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
3031                                              (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
3032 
3033         if( stream->timerID == 0 )
3034         {
3035             stream->isActive = 0;
3036             result = paUnanticipatedHostError;
3037             PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3038             goto error;
3039         }
3040 #else
3041         /* Create processing thread which calls TimerCallback */
3042 
3043         stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
3044         if( !stream->processingThread )
3045         {
3046             result = paUnanticipatedHostError;
3047             PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3048             goto error;
3049         }
3050 
3051         if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
3052         {
3053             result = paUnanticipatedHostError;
3054             PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3055             goto error;
3056         }
3057 #endif
3058     }
3059 
3060     stream->isActive = 1;
3061     stream->isStarted = 1;
3062 
3063     assert( result == paNoError );
3064     return result;
3065 
3066 error:
3067 
3068     if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning )
3069         IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
3070     stream->outputIsRunning = FALSE;
3071 
3072 #ifndef PA_WIN_DS_USE_WMME_TIMER
3073     if( stream->processingThread )
3074     {
3075 #ifdef CLOSE_THREAD_HANDLE
3076         CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */
3077 #endif
3078         stream->processingThread = NULL;
3079     }
3080 #endif
3081 
3082     return result;
3083 }
3084 
3085 
3086 /***********************************************************************************/
StopStream(PaStream * s)3087 static PaError StopStream( PaStream *s )
3088 {
3089     PaError result = paNoError;
3090     PaWinDsStream *stream = (PaWinDsStream*)s;
3091     HRESULT          hr;
3092     int timeoutMsec;
3093 
3094     if( stream->streamRepresentation.streamCallback )
3095     {
3096         stream->stopProcessing = 1;
3097 
3098         /* Set timeout at 4 times maximum time we might wait. */
3099         timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
3100 
3101         WaitForSingleObject( stream->processingCompleted, timeoutMsec );
3102     }
3103 
3104 #ifdef PA_WIN_DS_USE_WMME_TIMER
3105     if( stream->timerID != 0 )
3106     {
3107         timeKillEvent(stream->timerID);  /* Stop callback timer. */
3108         stream->timerID = 0;
3109     }
3110 #else
3111     if( stream->processingThread )
3112     {
3113         if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT )
3114             return paUnanticipatedHostError;
3115 
3116 #ifdef CLOSE_THREAD_HANDLE
3117         CloseHandle( stream->processingThread ); /* Delete thread. */
3118         stream->processingThread = NULL;
3119 #endif
3120 
3121     }
3122 #endif
3123 
3124     if( stream->systemTimerResolutionPeriodMs > 0 ){
3125         timeEndPeriod( stream->systemTimerResolutionPeriodMs );
3126         stream->systemTimerResolutionPeriodMs = 0;
3127     }
3128 
3129     if( stream->bufferProcessor.outputChannelCount > 0 )
3130     {
3131         // Stop the buffer playback
3132         if( stream->pDirectSoundOutputBuffer != NULL )
3133         {
3134             stream->outputIsRunning = FALSE;
3135             // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
3136             hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
3137 
3138             if( stream->pDirectSoundPrimaryBuffer )
3139                 IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
3140         }
3141     }
3142 
3143     if( stream->bufferProcessor.inputChannelCount > 0 )
3144     {
3145         // Stop the buffer capture
3146         if( stream->pDirectSoundInputBuffer != NULL )
3147         {
3148             // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
3149             hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
3150         }
3151     }
3152 
3153     stream->isStarted = 0;
3154 
3155     return result;
3156 }
3157 
3158 
3159 /***********************************************************************************/
AbortStream(PaStream * s)3160 static PaError AbortStream( PaStream *s )
3161 {
3162     PaWinDsStream *stream = (PaWinDsStream*)s;
3163 
3164     stream->abortProcessing = 1;
3165     return StopStream( s );
3166 }
3167 
3168 
3169 /***********************************************************************************/
IsStreamStopped(PaStream * s)3170 static PaError IsStreamStopped( PaStream *s )
3171 {
3172     PaWinDsStream *stream = (PaWinDsStream*)s;
3173 
3174     return !stream->isStarted;
3175 }
3176 
3177 
3178 /***********************************************************************************/
IsStreamActive(PaStream * s)3179 static PaError IsStreamActive( PaStream *s )
3180 {
3181     PaWinDsStream *stream = (PaWinDsStream*)s;
3182 
3183     return stream->isActive;
3184 }
3185 
3186 /***********************************************************************************/
GetStreamTime(PaStream * s)3187 static PaTime GetStreamTime( PaStream *s )
3188 {
3189     /* suppress unused variable warnings */
3190     (void) s;
3191 
3192     return PaUtil_GetTime();
3193 }
3194 
3195 
3196 /***********************************************************************************/
GetStreamCpuLoad(PaStream * s)3197 static double GetStreamCpuLoad( PaStream* s )
3198 {
3199     PaWinDsStream *stream = (PaWinDsStream*)s;
3200 
3201     return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3202 }
3203 
3204 
3205 /***********************************************************************************
3206     As separate stream interfaces are used for blocking and callback
3207     streams, the following functions can be guaranteed to only be called
3208     for blocking streams.
3209 */
3210 
ReadStream(PaStream * s,void * buffer,unsigned long frames)3211 static PaError ReadStream( PaStream* s,
3212                            void *buffer,
3213                            unsigned long frames )
3214 {
3215     PaWinDsStream *stream = (PaWinDsStream*)s;
3216 
3217     /* suppress unused variable warnings */
3218     (void) buffer;
3219     (void) frames;
3220     (void) stream;
3221 
3222     /* IMPLEMENT ME, see portaudio.h for required behavior*/
3223 
3224     return paNoError;
3225 }
3226 
3227 
3228 /***********************************************************************************/
WriteStream(PaStream * s,const void * buffer,unsigned long frames)3229 static PaError WriteStream( PaStream* s,
3230                             const void *buffer,
3231                             unsigned long frames )
3232 {
3233     PaWinDsStream *stream = (PaWinDsStream*)s;
3234 
3235     /* suppress unused variable warnings */
3236     (void) buffer;
3237     (void) frames;
3238     (void) stream;
3239 
3240     /* IMPLEMENT ME, see portaudio.h for required behavior*/
3241 
3242     return paNoError;
3243 }
3244 
3245 
3246 /***********************************************************************************/
GetStreamReadAvailable(PaStream * s)3247 static signed long GetStreamReadAvailable( PaStream* s )
3248 {
3249     PaWinDsStream *stream = (PaWinDsStream*)s;
3250 
3251     /* suppress unused variable warnings */
3252     (void) stream;
3253 
3254     /* IMPLEMENT ME, see portaudio.h for required behavior*/
3255 
3256     return 0;
3257 }
3258 
3259 
3260 /***********************************************************************************/
GetStreamWriteAvailable(PaStream * s)3261 static signed long GetStreamWriteAvailable( PaStream* s )
3262 {
3263     PaWinDsStream *stream = (PaWinDsStream*)s;
3264 
3265     /* suppress unused variable warnings */
3266     (void) stream;
3267 
3268     /* IMPLEMENT ME, see portaudio.h for required behavior*/
3269 
3270     return 0;
3271 }
3272 
3273 #endif
3274