Ticket #54: pa_win_ds.c

File pa_win_ds.c, 82.3 KB (added by alec_rogers, 16 months ago)
Line 
1/*
2 * $Id: pa_win_ds.c 1116 2006-09-06 15:47:03Z rossb $
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-2006 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 hostaip_src
42
43    @todo implement paInputOverflow callback status flag
44   
45    @todo implement paNeverDropInput.
46
47    @todo implement host api specific extension to set i/o buffer sizes in frames
48
49    @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
50
51    @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
52
53    @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
54        a native portaudio error code. Standard DirectSound result codes are documented at msdn.
55
56    @todo implement IsFormatSupported
57
58    @todo check that CoInitialize() CoUninitialize() are always correctly
59        paired, even in error cases.
60
61    @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
62
63    @todo make sure all buffers have been played before stopping the stream
64        when the stream callback returns paComplete
65
66    old TODOs from phil, need to work out if these have been done:
67        O- fix "patest_stop.c"
68*/
69
70#include <stdio.h>
71#include <string.h> /* strlen() */
72
73// Include the KS headers for WAVEFORMATEXTENSIBLE
74#define _INC_MMREG // This is required for certain guids in ksmedia
75#include <windows.h>
76#include <ks.h>
77#include <ksmedia.h>
78
79#include "pa_util.h"
80#include "pa_allocation.h"
81#include "pa_hostapi.h"
82#include "pa_stream.h"
83#include "pa_cpuload.h"
84#include "pa_process.h"
85
86#include "pa_win_ds_dynlink.h"
87
88#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
89#pragma comment( lib, "dsound.lib" )
90#pragma comment( lib, "winmm.lib" )
91#endif
92
93/*
94 provided in newer platform sdks and x64
95 */
96#ifndef DWORD_PTR
97#define DWORD_PTR DWORD
98#endif
99
100/* Define the maximum number of channels to be two.
101 * Increasing this number will give you multi-channel capacity at the expense
102 * of inaccurate reporting of the number of maximum output channels.
103 */
104#ifndef PA_DS_MAX_CHANS
105#define PA_DS_MAX_CHANS 2
106#endif
107
108#define PRINT(x) PA_DEBUG(x);
109#define ERR_RPT(x) PRINT(x)
110#define DBUG(x)   PRINT(x)
111#define DBUGX(x)  PRINT(x)
112
113#define PA_USE_HIGH_LATENCY   (0)
114#if PA_USE_HIGH_LATENCY
115#define PA_WIN_9X_LATENCY     (500)
116#define PA_WIN_NT_LATENCY     (600)
117#else
118#define PA_WIN_9X_LATENCY     (140)
119#define PA_WIN_NT_LATENCY     (280)
120#endif
121
122#define PA_WIN_WDM_LATENCY       (120)
123
124#define SECONDS_PER_MSEC      (0.001)
125#define MSEC_PER_SECOND       (1000)
126
127/* prototypes for functions declared in this file */
128
129#ifdef __cplusplus
130extern "C"
131{
132#endif /* __cplusplus */
133
134PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
135
136#ifdef __cplusplus
137}
138#endif /* __cplusplus */
139
140static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
141static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
142                           PaStream** s,
143                           const PaStreamParameters *inputParameters,
144                           const PaStreamParameters *outputParameters,
145                           double sampleRate,
146                           unsigned long framesPerBuffer,
147                           PaStreamFlags streamFlags,
148                           PaStreamCallback *streamCallback,
149                           void *userData );
150static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
151                                  const PaStreamParameters *inputParameters,
152                                  const PaStreamParameters *outputParameters,
153                                  double sampleRate );
154static PaError CloseStream( PaStream* stream );
155static PaError StartStream( PaStream *stream );
156static PaError StopStream( PaStream *stream );
157static PaError AbortStream( PaStream *stream );
158static PaError IsStreamStopped( PaStream *s );
159static PaError IsStreamActive( PaStream *stream );
160static PaTime GetStreamTime( PaStream *stream );
161static double GetStreamCpuLoad( PaStream* stream );
162static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
163static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
164static signed long GetStreamReadAvailable( PaStream* stream );
165static signed long GetStreamWriteAvailable( PaStream* stream );
166
167
168/* FIXME: should convert hr to a string */
169#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
170    PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
171
172/************************************************* DX Prototypes **********/
173static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
174                                     LPCTSTR lpszDesc,
175                                     LPCTSTR lpszDrvName,
176                                     LPVOID lpContext );
177
178/************************************************************************************/
179/********************** Structures **************************************************/
180/************************************************************************************/
181/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
182
183typedef struct PaWinDsDeviceInfo
184{
185    GUID                             guid;
186    GUID                            *lpGUID;
187    double                           sampleRates[3];
188} PaWinDsDeviceInfo;
189
190typedef struct
191{
192    PaUtilHostApiRepresentation inheritedHostApiRep;
193    PaUtilStreamInterface    callbackStreamInterface;
194    PaUtilStreamInterface    blockingStreamInterface;
195
196    PaUtilAllocationGroup   *allocations;
197
198    /* implementation specific data goes here */
199    PaWinDsDeviceInfo       *winDsDeviceInfos;
200
201} PaWinDsHostApiRepresentation;
202
203
204/* PaWinDsStream - a stream data structure specifically for this implementation */
205
206typedef struct PaWinDsStream
207{
208    PaUtilStreamRepresentation streamRepresentation;
209    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
210    PaUtilBufferProcessor bufferProcessor;
211
212/* DirectSound specific data. */
213
214/* Output */
215    LPDIRECTSOUND        pDirectSound;
216    LPDIRECTSOUNDBUFFER  pDirectSoundOutputBuffer;
217    DWORD                outputBufferWriteOffsetBytes;     /* last write position */
218    INT                  outputBufferSizeBytes;
219    INT                  bytesPerOutputFrame;
220    /* Try to detect play buffer underflows. */
221    LARGE_INTEGER        perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
222    LARGE_INTEGER        previousPlayTime;
223    UINT                 previousPlayCursor;
224    UINT                 outputUnderflowCount;
225    BOOL                 outputIsRunning;
226    /* use double which lets us can play for several thousand years with enough precision */
227    double               dsw_framesWritten;
228    double               framesPlayed;
229/* Input */
230    LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
231    LPDIRECTSOUNDCAPTUREBUFFER   pDirectSoundInputBuffer;
232    INT                  bytesPerInputFrame;
233    UINT                 readOffset;      /* last read position */
234    UINT                 inputSize;
235
236   
237    MMRESULT         timerID;
238    int              framesPerDSBuffer;
239    double           framesWritten;
240    double           secondsPerHostByte; /* Used to optimize latency calculation for outTime */
241
242    PaStreamCallbackFlags callbackFlags;
243   
244/* FIXME - move all below to PaUtilStreamRepresentation */
245    volatile int     isStarted;
246    volatile int     isActive;
247    volatile int     stopProcessing; /* stop thread once existing buffers have been returned */
248    volatile int     abortProcessing; /* stop thread immediately */
249} PaWinDsStream;
250
251
252/************************************************************************************
253** Duplicate the input string using the allocations allocator.
254** A NULL string is converted to a zero length string.
255** If memory cannot be allocated, NULL is returned.
256**/
257static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
258{
259    char *result = 0;
260   
261    if( src != NULL )
262    {
263        size_t len = strlen(src);
264        result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
265        if( result )
266            memcpy( (void *) result, src, len+1 );
267    }
268    else
269    {
270        result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
271        if( result )
272            result[0] = '\0';
273    }
274
275    return result;
276}
277
278/************************************************************************************
279** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
280** information during device enumeration.
281*/
282typedef struct DSDeviceNameAndGUID{
283    char *name; // allocated from parent's allocations, never deleted by this structure
284    GUID guid;
285    LPGUID lpGUID;
286} DSDeviceNameAndGUID;
287
288typedef struct DSDeviceNameAndGUIDVector{
289    PaUtilAllocationGroup *allocations;
290    PaError enumerationError;
291
292    int count;
293    int free;
294    DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
295} DSDeviceNameAndGUIDVector;
296
297static PaError InitializeDSDeviceNameAndGUIDVector(
298        DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
299{
300    PaError result = paNoError;
301
302    guidVector->allocations = allocations;
303    guidVector->enumerationError = paNoError;
304
305    guidVector->count = 0;
306    guidVector->free = 8;
307    guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
308    if( guidVector->items == NULL )
309        result = paInsufficientMemory;
310   
311    return result;
312}
313
314static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
315{
316    PaError result = paNoError;
317    DSDeviceNameAndGUID *newItems;
318    int i;
319   
320    /* double size of vector */
321    int size = guidVector->count + guidVector->free;
322    guidVector->free += size;
323
324    newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
325    if( newItems == NULL )
326    {
327        result = paInsufficientMemory;
328    }
329    else
330    {
331        for( i=0; i < guidVector->count; ++i )
332        {
333            newItems[i].name = guidVector->items[i].name;
334            if( guidVector->items[i].lpGUID == NULL )
335            {
336                newItems[i].lpGUID = NULL;
337            }
338            else
339            {
340                newItems[i].lpGUID = &newItems[i].guid;
341                memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
342            }
343        }
344
345        LocalFree( guidVector->items );
346        guidVector->items = newItems;
347    }                               
348
349    return result;
350}
351
352/*
353    it's safe to call DSDeviceNameAndGUIDVector multiple times
354*/
355static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
356{
357    PaError result = paNoError;
358
359    if( guidVector->items != NULL )
360    {
361        if( LocalFree( guidVector->items ) != NULL )
362            result = paInsufficientMemory;              /** @todo this isn't the correct error to return from a deallocation failure */
363
364        guidVector->items = NULL;
365    }
366
367    return result;
368}
369
370/************************************************************************************
371** Collect preliminary device information during DirectSound enumeration
372*/
373static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
374                                     LPCTSTR lpszDesc,
375                                     LPCTSTR lpszDrvName,
376                                     LPVOID lpContext )
377{
378    DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
379    PaError error;
380
381    (void) lpszDrvName; /* unused variable */
382
383    if( namesAndGUIDs->free == 0 )
384    {
385        error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
386        if( error != paNoError )
387        {
388            namesAndGUIDs->enumerationError = error;
389            return FALSE;
390        }
391    }
392   
393    /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
394    if( lpGUID == NULL )
395    {
396        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
397    }
398    else
399    {
400        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
401                &namesAndGUIDs->items[namesAndGUIDs->count].guid;
402     
403        memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
404    }
405
406    namesAndGUIDs->items[namesAndGUIDs->count].name =
407            DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
408    if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
409    {
410        namesAndGUIDs->enumerationError = paInsufficientMemory;
411        return FALSE;
412    }
413
414    ++namesAndGUIDs->count;
415    --namesAndGUIDs->free;
416   
417    return TRUE;
418}
419
420
421/*
422    GUIDs for emulated devices which we blacklist below.
423    are there more than two of them??
424*/
425
426GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
427GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
428
429
430#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
431static double defaultSampleRateSearchOrder_[] =
432    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
433        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
434
435
436/************************************************************************************
437** Extract capabilities from an output device, and add it to the device info list
438** if successful. This function assumes that there is enough room in the
439** device info list to accomodate all entries.
440**
441** The device will not be added to the device list if any errors are encountered.
442*/
443static PaError AddOutputDeviceInfoFromDirectSound(
444        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
445{
446    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
447    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
448    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
449    HRESULT                       hr;
450    LPDIRECTSOUND                 lpDirectSound;
451    DSCAPS                        caps;
452    int                           deviceOK = TRUE;
453    PaError                       result = paNoError;
454    int                           i;
455   
456    /* Copy GUID to the device info structure. Set pointer. */
457    if( lpGUID == NULL )
458    {
459        winDsDeviceInfo->lpGUID = NULL;
460    }
461    else
462    {
463        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
464        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
465    }
466
467   
468    if( lpGUID )
469    {
470        if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
471            IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
472        {
473            PA_DEBUG(("BLACKLISTED: %s \n",name));
474            return paNoError;
475        }
476    }
477
478    /* Create a DirectSound object for the specified GUID
479        Note that using CoCreateInstance doesn't work on windows CE.
480    */
481    hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
482
483    /** try using CoCreateInstance because DirectSoundCreate was hanging under
484        some circumstances - note this was probably related to the
485        #define BOOL short bug which has now been fixed
486        @todo delete this comment and the following code once we've ensured
487        there is no bug.
488    */
489    /*
490    hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
491            &IID_IDirectSound, (void**)&lpDirectSound );
492
493    if( hr == S_OK )
494    {
495        hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
496    }
497    */
498   
499    if( hr != DS_OK )
500    {
501        if (hr == DSERR_ALLOCATED)
502            PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
503        DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
504        if (lpGUID)
505            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",
506                 name,
507                 lpGUID->Data1,
508                 lpGUID->Data2,
509                 lpGUID->Data3,
510                 lpGUID->Data4[0],
511                 lpGUID->Data4[1],
512                 lpGUID->Data4[2],
513                 lpGUID->Data4[3],
514                 lpGUID->Data4[4],
515                 lpGUID->Data4[5],
516                 lpGUID->Data4[6],
517                 lpGUID->Data4[7]));
518
519        deviceOK = FALSE;
520    }
521    else
522    {
523        /* Query device characteristics. */
524        memset( &caps, 0, sizeof(caps) );
525        caps.dwSize = sizeof(caps);
526        hr = IDirectSound_GetCaps( lpDirectSound, &caps );
527        if( hr != DS_OK )
528        {
529            DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
530            deviceOK = FALSE;
531        }
532        else
533        {
534
535#ifndef PA_NO_WMME
536            if( caps.dwFlags & DSCAPS_EMULDRIVER )
537            {
538                /* If WMME supported, then reject Emulated drivers because they are lousy. */
539                deviceOK = FALSE;
540            }
541#endif
542
543            if( deviceOK )
544            {
545                deviceInfo->maxInputChannels = 0;
546                /* Mono or stereo device? */
547                deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? PA_DS_MAX_CHANS : 1;
548
549                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
550                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
551                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
552                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
553               
554                /* initialize defaultSampleRate */
555               
556                if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
557                {
558                    /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
559                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
560
561                    for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
562                    {
563                        if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
564                                && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
565
566                            deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
567                            break;
568                        }
569                    }
570                }
571                else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
572                {
573                    if( caps.dwMinSecondarySampleRate == 0 )
574                    {
575                        /*
576                        ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
577                        ** But it supports continuous sampling.
578                        ** So fake range of rates, and hope it really supports it.
579                        */
580                        deviceInfo->defaultSampleRate = 44100.0f;
581
582                        DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
583                    }
584                    else
585                    {
586                            deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
587                    }
588                }
589                else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
590                {
591                    /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
592                    ** But we know that they really support a range of rates!
593                    ** So when we see a ridiculous set of rates, assume it is a range.
594                    */
595                  deviceInfo->defaultSampleRate = 44100.0f;
596                  DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
597                }
598                else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
599
600
601                //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
602                // dwFlags | DSCAPS_CONTINUOUSRATE
603            }
604        }
605
606        IDirectSound_Release( lpDirectSound );
607    }
608
609    if( deviceOK )
610    {
611        deviceInfo->name = name;
612
613        if( lpGUID == NULL )
614            hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
615           
616        hostApi->info.deviceCount++;
617    }
618
619    return result;
620}
621
622
623/************************************************************************************
624** Extract capabilities from an input device, and add it to the device info list
625** if successful. This function assumes that there is enough room in the
626** device info list to accomodate all entries.
627**
628** The device will not be added to the device list if any errors are encountered.
629*/
630static PaError AddInputDeviceInfoFromDirectSoundCapture(
631        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
632{
633    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
634    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
635    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
636    HRESULT                       hr;
637    LPDIRECTSOUNDCAPTURE          lpDirectSoundCapture;
638    DSCCAPS                       caps;
639    int                           deviceOK = TRUE;
640    PaError                       result = paNoError;
641   
642    /* Copy GUID to the device info structure. Set pointer. */
643    if( lpGUID == NULL )
644    {
645        winDsDeviceInfo->lpGUID = NULL;
646    }
647    else
648    {
649        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
650        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
651    }
652
653
654    hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
655
656    /** try using CoCreateInstance because DirectSoundCreate was hanging under
657        some circumstances - note this was probably related to the
658        #define BOOL short bug which has now been fixed
659        @todo delete this comment and the following code once we've ensured
660        there is no bug.
661    */
662    /*
663    hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
664            &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
665    */
666    if( hr != DS_OK )
667    {
668        DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
669        deviceOK = FALSE;
670    }
671    else
672    {
673        /* Query device characteristics. */
674        memset( &caps, 0, sizeof(caps) );
675        caps.dwSize = sizeof(caps);
676        hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
677        if( hr != DS_OK )
678        {
679            DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
680            deviceOK = FALSE;
681        }
682        else
683        {
684#ifndef PA_NO_WMME
685            if( caps.dwFlags & DSCAPS_EMULDRIVER )
686            {
687                /* If WMME supported, then reject Emulated drivers because they are lousy. */
688                deviceOK = FALSE;
689            }
690#endif
691
692            if( deviceOK )
693            {
694                deviceInfo->maxInputChannels = caps.dwChannels;
695                deviceInfo->maxOutputChannels = 0;
696
697                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
698                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
699                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
700                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
701
702/*  constants from a WINE patch by Francois Gouget, see:
703    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
704
705    ---
706    Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
707    From: Francois Gouget <fgouget@ ... .fr>
708    To: Ross Bencina <rbencina@ ... .au>
709    Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
710
711    [snip]
712
713    I give you permission to use the patch below under the BSD license.
714    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
715
716    [snip]
717*/
718#ifndef WAVE_FORMAT_48M08
719#define WAVE_FORMAT_48M08      0x00001000    /* 48     kHz, Mono,   8-bit  */
720#define WAVE_FORMAT_48S08      0x00002000    /* 48     kHz, Stereo, 8-bit  */
721#define WAVE_FORMAT_48M16      0x00004000    /* 48     kHz, Mono,   16-bit */
722#define WAVE_FORMAT_48S16      0x00008000    /* 48     kHz, Stereo, 16-bit */
723#define WAVE_FORMAT_96M08      0x00010000    /* 96     kHz, Mono,   8-bit  */
724#define WAVE_FORMAT_96S08      0x00020000    /* 96     kHz, Stereo, 8-bit  */
725#define WAVE_FORMAT_96M16      0x00040000    /* 96     kHz, Mono,   16-bit */
726#define WAVE_FORMAT_96S16      0x00080000    /* 96     kHz, Stereo, 16-bit */
727#endif
728
729                /* defaultSampleRate */
730                if( caps.dwChannels == 2 )
731                {
732                    if( caps.dwFormats & WAVE_FORMAT_4S16 )
733                        deviceInfo->defaultSampleRate = 44100.0;
734                    else if( caps.dwFormats & WAVE_FORMAT_48S16 )
735                        deviceInfo->defaultSampleRate = 48000.0;
736                    else if( caps.dwFormats & WAVE_FORMAT_2S16 )
737                        deviceInfo->defaultSampleRate = 22050.0;
738                    else if( caps.dwFormats & WAVE_FORMAT_1S16 )
739                        deviceInfo->defaultSampleRate = 11025.0;
740                    else if( caps.dwFormats & WAVE_FORMAT_96S16 )
741                        deviceInfo->defaultSampleRate = 96000.0;
742                    else
743                        deviceInfo->defaultSampleRate = 0.;
744                }
745                else if( caps.dwChannels == 1 )
746                {
747                    if( caps.dwFormats & WAVE_FORMAT_4M16 )
748                        deviceInfo->defaultSampleRate = 44100.0;
749                    else if( caps.dwFormats & WAVE_FORMAT_48M16 )
750                        deviceInfo->defaultSampleRate = 48000.0;
751                    else if( caps.dwFormats & WAVE_FORMAT_2M16 )
752                        deviceInfo->defaultSampleRate = 22050.0;
753                    else if( caps.dwFormats & WAVE_FORMAT_1M16 )
754                        deviceInfo->defaultSampleRate = 11025.0;
755                    else if( caps.dwFormats & WAVE_FORMAT_96M16 )
756                        deviceInfo->defaultSampleRate = 96000.0;
757                    else
758                        deviceInfo->defaultSampleRate = 0.;
759                }
760                else deviceInfo->defaultSampleRate = 0.;
761            }
762        }
763       
764        IDirectSoundCapture_Release( lpDirectSoundCapture );
765    }
766
767    if( deviceOK )
768    {
769        deviceInfo->name = name;
770
771        if( lpGUID == NULL )
772            hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
773
774        hostApi->info.deviceCount++;
775    }
776
777    return result;
778}
779
780
781/***********************************************************************************/
782PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
783{
784    PaError result = paNoError;
785    int i, deviceCount;
786    PaWinDsHostApiRepresentation *winDsHostApi;
787    DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
788    PaDeviceInfo *deviceInfoArray;
789
790    HRESULT hr = CoInitialize(NULL);        /** @todo: should uninitialize too */
791    if( FAILED(hr) ){
792        return paUnanticipatedHostError;
793    }           
794
795    /* initialise guid vectors so they can be safely deleted on error */
796    inputNamesAndGUIDs.items = NULL;
797    outputNamesAndGUIDs.items = NULL;
798
799    PaWinDs_InitializeDSoundEntryPoints();
800
801    winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
802    if( !winDsHostApi )
803    {
804        result = paInsufficientMemory;
805        goto error;
806    }
807
808    winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
809    if( !winDsHostApi->allocations )
810    {
811        result = paInsufficientMemory;
812        goto error;
813    }
814
815    *hostApi = &winDsHostApi->inheritedHostApiRep;
816    (*hostApi)->info.structVersion = 1;
817    (*hostApi)->info.type = paDirectSound;
818    (*hostApi)->info.name = "Windows DirectSound";
819   
820    (*hostApi)->info.deviceCount = 0;
821    (*hostApi)->info.defaultInputDevice = paNoDevice;
822    (*hostApi)->info.defaultOutputDevice = paNoDevice;
823
824   
825/* DSound - enumerate devices to count them and to gather their GUIDs */
826
827
828    result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
829    if( result != paNoError )
830        goto error;
831
832    result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
833    if( result != paNoError )
834        goto error;
835
836    paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
837
838    paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
839
840    if( inputNamesAndGUIDs.enumerationError != paNoError )
841    {
842        result = inputNamesAndGUIDs.enumerationError;
843        goto error;
844    }
845
846    if( outputNamesAndGUIDs.enumerationError != paNoError )
847    {
848        result = outputNamesAndGUIDs.enumerationError;
849        goto error;
850    }
851
852    deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
853
854    if( deviceCount > 0 )
855    {
856        /* allocate array for pointers to PaDeviceInfo structs */
857        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
858                winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
859        if( !(*hostApi)->deviceInfos )
860        {
861            result = paInsufficientMemory;
862            goto error;
863        }
864
865        /* allocate all PaDeviceInfo structs in a contiguous block */
866        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
867                winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
868        if( !deviceInfoArray )
869        {
870            result = paInsufficientMemory;
871            goto error;
872        }
873
874        /* allocate all DSound specific info structs in a contiguous block */
875        winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
876                winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
877        if( !winDsHostApi->winDsDeviceInfos )
878        {
879            result = paInsufficientMemory;
880            goto error;
881        }
882
883        for( i=0; i < deviceCount; ++i )
884        {
885            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
886            deviceInfo->structVersion = 2;
887            deviceInfo->hostApi = hostApiIndex;
888            deviceInfo->name = 0;
889            (*hostApi)->deviceInfos[i] = deviceInfo;
890        }
891
892        for( i=0; i< inputNamesAndGUIDs.count; ++i )
893        {
894            result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
895                    inputNamesAndGUIDs.items[i].name,
896                    inputNamesAndGUIDs.items[i].lpGUID );
897            if( result != paNoError )
898                goto error;
899        }
900
901        for( i=0; i< outputNamesAndGUIDs.count; ++i )
902        {
903            result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
904                    outputNamesAndGUIDs.items[i].name,
905                    outputNamesAndGUIDs.items[i].lpGUID );
906            if( result != paNoError )
907                goto error;
908        }
909    }   
910
911    result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
912    if( result != paNoError )
913        goto error;
914
915    result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
916    if( result != paNoError )
917        goto error;
918
919   
920    (*hostApi)->Terminate = Terminate;
921    (*hostApi)->OpenStream = OpenStream;
922    (*hostApi)->IsFormatSupported = IsFormatSupported;
923
924    PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
925                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
926                                      GetStreamTime, GetStreamCpuLoad,
927                                      PaUtil_DummyRead, PaUtil_DummyWrite,
928                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
929
930    PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
931                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
932                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
933                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
934
935    return result;
936
937error:
938    if( winDsHostApi )
939    {
940        if( winDsHostApi->allocations )
941        {
942            PaUtil_FreeAllAllocations( winDsHostApi->allocations );
943            PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
944        }
945               
946        PaUtil_FreeMemory( winDsHostApi );
947    }
948
949    TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
950    TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
951
952    return result;
953}
954
955
956/***********************************************************************************/
957static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
958{
959    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
960
961    /*
962        IMPLEMENT ME:
963            - clean up any resources not handled by the allocation group
964    */
965
966    if( winDsHostApi->allocations )
967    {
968        PaUtil_FreeAllAllocations( winDsHostApi->allocations );
969        PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
970    }
971
972    PaUtil_FreeMemory( winDsHostApi );
973
974    PaWinDs_TerminateDSoundEntryPoints();
975
976    CoUninitialize();
977}
978
979
980/* Set minimal latency based on whether NT or Win95.
981 * NT has higher latency.
982 */
983static int PaWinDS_GetMinSystemLatency( void )
984{
985    int minLatencyMsec;
986    /* Set minimal latency based on whether NT or other OS.
987     * NT has higher latency.
988     */
989    OSVERSIONINFO osvi;
990        osvi.dwOSVersionInfoSize = sizeof( osvi );
991        GetVersionEx( &osvi );
992    DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
993    DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
994    DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
995    /* Check for NT */
996        if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
997        {
998                minLatencyMsec = PA_WIN_NT_LATENCY;
999        }
1000        else if(osvi.dwMajorVersion >= 5)
1001        {
1002                minLatencyMsec = PA_WIN_WDM_LATENCY;
1003        }
1004        else
1005        {
1006                minLatencyMsec = PA_WIN_9X_LATENCY;
1007        }
1008    return minLatencyMsec;
1009}
1010
1011/***********************************************************************************/
1012static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1013                                  const PaStreamParameters *inputParameters,
1014                                  const PaStreamParameters *outputParameters,
1015                                  double sampleRate )
1016{
1017    int inputChannelCount, outputChannelCount;
1018    PaSampleFormat inputSampleFormat, outputSampleFormat;
1019   
1020    if( inputParameters )
1021    {
1022        inputChannelCount = inputParameters->channelCount;
1023        inputSampleFormat = inputParameters->sampleFormat;
1024
1025        /* unless alternate device specification is supported, reject the use of
1026            paUseHostApiSpecificDeviceSpecification */
1027
1028        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1029            return paInvalidDevice;
1030
1031        /* check that input device can support inputChannelCount */
1032        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1033            return paInvalidChannelCount;
1034
1035        /* validate inputStreamInfo */
1036        if( inputParameters->hostApiSpecificStreamInfo )
1037            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1038    }
1039    else
1040    {
1041        inputChannelCount = 0;
1042    }
1043
1044    if( outputParameters )
1045    {
1046        outputChannelCount = outputParameters->channelCount;
1047        outputSampleFormat = outputParameters->sampleFormat;
1048       
1049        /* unless alternate device specification is supported, reject the use of
1050            paUseHostApiSpecificDeviceSpecification */
1051
1052        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1053            return paInvalidDevice;
1054
1055        /* check that output device can support inputChannelCount */
1056        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1057            return paInvalidChannelCount;
1058
1059        /* validate outputStreamInfo */
1060        if( outputParameters->hostApiSpecificStreamInfo )
1061            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1062    }
1063    else
1064    {
1065        outputChannelCount = 0;
1066    }
1067   
1068    /*
1069        IMPLEMENT ME:
1070
1071            - if a full duplex stream is requested, check that the combination
1072                of input and output parameters is supported if necessary
1073
1074            - check that the device supports sampleRate
1075
1076        Because the buffer adapter handles conversion between all standard
1077        sample formats, the following checks are only required if paCustomFormat
1078        is implemented, or under some other unusual conditions.
1079
1080            - check that input device can support inputSampleFormat, or that
1081                we have the capability to convert from outputSampleFormat to
1082                a native format
1083
1084            - check that output device can support outputSampleFormat, or that
1085                we have the capability to convert from outputSampleFormat to
1086                a native format
1087    */
1088
1089    return paFormatIsSupported;
1090}
1091
1092
1093/*************************************************************************
1094** Determine minimum number of buffers required for this host based
1095** on minimum latency. Latency can be optionally set by user by setting
1096** an environment variable. For example, to set latency to 200 msec, put:
1097**
1098**    set PA_MIN_LATENCY_MSEC=200
1099**
1100** in the AUTOEXEC.BAT file and reboot.
1101** If the environment variable is not set, then the latency will be determined
1102** based on the OS. Windows NT has higher latency than Win95.
1103*/
1104#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
1105#define PA_ENV_BUF_SIZE  (32)
1106
1107static int PaWinDs_GetMinLatencyFrames( double sampleRate )
1108{
1109    char      envbuf[PA_ENV_BUF_SIZE];
1110    DWORD     hresult;
1111    int       minLatencyMsec = 0;
1112
1113    /* Let user determine minimal latency by setting environment variable. */
1114    hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
1115    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
1116    {
1117        minLatencyMsec = atoi( envbuf );
1118    }
1119    else
1120    {
1121        minLatencyMsec = PaWinDS_GetMinSystemLatency();
1122#if PA_USE_HIGH_LATENCY
1123        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
1124#endif
1125
1126    }
1127
1128    return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
1129}
1130
1131/* Initialize a WAVEFORMATEXTENSIBLE structure, possibly using only those fields found in a WAVEFORMATEX */
1132static void initWaveformat(WAVEFORMATEXTENSIBLE *pwfFormat, BOOL bUseExtensible,
1133                           PaSampleFormat sampleFormat, int nChannels, unsigned long nFrameRate)
1134{
1135    int bytesPerSample = Pa_GetSampleSize(sampleFormat);
1136    int bitsPerSample  = bytesPerSample*8;
1137
1138  /* Initialize the fields common to both structures */
1139    pwfFormat->Format.nChannels       = nChannels;
1140    pwfFormat->Format.nSamplesPerSec  = nFrameRate;
1141    pwfFormat->Format.wBitsPerSample  = bitsPerSample;
1142    pwfFormat->Format.nBlockAlign     = (WORD)(pwfFormat->Format.nChannels * bytesPerSample);
1143    pwfFormat->Format.nAvgBytesPerSec = pwfFormat->Format.nSamplesPerSec * pwfFormat->Format.nBlockAlign;
1144
1145    /* Initialize as WAVEFORMATEX */
1146    if(!bUseExtensible) {
1147        pwfFormat->Format.wFormatTag      = WAVE_FORMAT_PCM;
1148        pwfFormat->Format.cbSize          = 0;
1149    }
1150
1151    /* Initialize as WAVEFORMATEXTENSIBLE */
1152    else {
1153        int i;
1154        pwfFormat->Format.wFormatTag           = WAVE_FORMAT_EXTENSIBLE;
1155        pwfFormat->Format.cbSize               = 22;
1156        pwfFormat->Samples.wValidBitsPerSample = pwfFormat->Format.wBitsPerSample;
1157        if(sampleFormat==paFloat32)
1158            pwfFormat->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1159        else
1160            pwfFormat->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1161        /* If the input is monophonic, send the data to the front center channel.
1162           Otherwise, fill out the channel mask such that channels are sent to the first N channels. */
1163        if(nChannels==1) {
1164            pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER;
1165        }
1166        else {
1167            pwfFormat->dwChannelMask = 0;
1168            for(i=0; i<nChannels; i++)
1169                pwfFormat->dwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1;
1170        }
1171    }
1172}
1173
1174static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
1175{
1176    DSCBUFFERDESC  captureDesc;
1177    WAVEFORMATEXTENSIBLE  wfFormat;
1178    HRESULT        result;
1179
1180    stream->bytesPerInputFrame = nChannels * Pa_GetSampleSize(sampleFormat);
1181    stream->inputSize = bytesPerBuffer;
1182    // ----------------------------------------------------------------------
1183    // Setup the secondary buffer description
1184    ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1185    captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1186    captureDesc.dwFlags =  0;
1187    captureDesc.dwBufferBytes = bytesPerBuffer;
1188    captureDesc.lpwfxFormat = (WAVEFORMATEX*)&wfFormat;
1189
1190    // Attempt to create the capture buffer with WAVEFORMATEXTENSIBLE
1191    initWaveformat(&wfFormat, TRUE, sampleFormat, nChannels, nFrameRate);
1192    result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1193                 &captureDesc, &stream->pDirectSoundInputBuffer, NULL);
1194    if(result != DS_OK) {
1195        // Attempt to create the capture buffer with WAVEFORMATEX
1196        initWaveformat(&wfFormat, FALSE, sampleFormat, nChannels, nFrameRate);
1197        result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1198                     &captureDesc, &stream->pDirectSoundInputBuffer, NULL);
1199    }
1200    if(result != DS_OK) return result;
1201    stream->readOffset = 0;  // reset last read position to start of buffer
1202    return DS_OK;
1203}
1204
1205
1206static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
1207{
1208    DWORD          dwDataLen;
1209    DWORD          playCursor;
1210    HRESULT        result;
1211    LPDIRECTSOUNDBUFFER pPrimaryBuffer;
1212    HWND           hWnd;
1213    HRESULT        hr;
1214    WAVEFORMATEXTENSIBLE wfFormat;
1215    DSBUFFERDESC   primaryDesc;
1216    DSBUFFERDESC   secondaryDesc;
1217    unsigned char* pDSBuffData;
1218    LARGE_INTEGER  counterFrequency;
1219    int bytesPerSample = Pa_GetSampleSize(sampleFormat);
1220
1221    stream->outputBufferSizeBytes = bytesPerBuffer;
1222    stream->outputIsRunning = FALSE;
1223    stream->outputUnderflowCount = 0;
1224    stream->dsw_framesWritten = 0;
1225    stream->bytesPerOutputFrame = nChannels * bytesPerSample;
1226
1227    // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
1228    // applications's window. Also if that window is closed before the Buffer is closed
1229    // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
1230    // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
1231    // hWnd = GetForegroundWindow();
1232    //
1233    //  FIXME: The example code I have on the net creates a hidden window that
1234    //      is managed by our code - I think we should do that - one hidden
1235    //      window for the whole of Pa_DS
1236    //
1237    hWnd = GetDesktopWindow();
1238
1239    // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
1240    // Exclusize also prevents unexpected sounds from other apps during a performance.
1241    if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
1242              hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
1243    {
1244        return hr;
1245    }
1246
1247    // -----------------------------------------------------------------------
1248    // Create primary buffer and set format just so we can specify our custom format.
1249    // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
1250    // Setup the primary buffer description
1251    ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
1252    primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
1253    primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
1254    primaryDesc.dwBufferBytes = 0;
1255    primaryDesc.lpwfxFormat   = NULL;
1256    // Create the buffer
1257    if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1258                  &primaryDesc, &pPrimaryBuffer, NULL))