gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

pa_win_ds.c (125668B)


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